Алгоритм массива «разделяй и властвуй» ++

Я пытаюсь реализовать функцию, которая будет просматривать каждый элемент массива и определять, больше ли этот конкретный элемент одного INT и меньше другого INT. Например:

Return true if Arr[5] is >i && < u

У меня есть это в качестве базового алгоритма, который работает, но я хочу создать более эффективный фрагмент кода, используя методологию «разделяй и властвуй», однако у меня возникают проблемы с использованием рекурсии, чтобы он учитывался, и все примеры, которые я видел дело только с одной точкой сравнения, а не с двумя. может кто прольет свет на ситуацию. (http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm)

Мой исходный код (линейный):

int SimpleSort(int length) 
{ 
    int A[] = {25,5,20,10,50}; 
    int i = 25; //Less Than int 
    u = 2; //Greater Than 
    for(int Count = 0; Count < length; Count++) //Counter 
    { 
        if (A[Count] <= i && A[Count] >= u) //Checker 
            return true; 
    } return false;
}

Пример кода из того, что я подобрал до сих пор (безуспешно после многих часов работы над разными вещами и с использованием другого примера кода:

int A[] = {5,10,25,30,50,100,200,500,1000,2000};
int i = 10; //Less Than
int u = 5;  //Greater Than


int min = 1;
int max = length;
int mid = (min+max)/2;

if (i < A[mid] && u > A[mid])
{
    min = mid + 1;

}
else
{
    max = mid - 1;
}
Until i <= A1[mid] && u >= A1[mid])

Если этот вопрос не ясен, извините, спросите, если вам нужно, чтобы я что-то уточнил.


person Unknown    schedule 08.11.2012    source источник
comment
Вы хотите определить, существует ли хотя бы одно значение в интервале (i,u)? Обратите внимание, что ваш i > u, который не может работать с самого начала!   -  person bitmask    schedule 08.11.2012
comment
Это было плохое объяснение от меня, и мне очень жаль, я плохо вставлю ответ своей более простой версии этого кода, я пытаюсь создать версию «разделяй и властвуй».   -  person Unknown    schedule 08.11.2012
comment
Я пытаюсь применить к этому методологию, о которой я упоминал ранее, чтобы повысить ее эффективность для больших массивов int SimpleSort(int length) { int A[] = {25,5,20,10,50}; интервал я = 25; // Меньше, чем int u = 2; // Больше, чем for(int Count = 0; Count ‹ length; Count++) //Counter { if (A[Count] ‹= i && A[Count] ›= u) //Checker { return true; } } вернуть ложь;   -  person Unknown    schedule 08.11.2012
comment
извините, у меня нет представителя, чтобы ответить на мои собственные вопросы .... так что придется идти сюда. это простая версия кода, который я пытаюсь улучшить.   -  person Unknown    schedule 08.11.2012
comment
Вы предполагаете, что во входном списке есть только ОДНО значение, которое соответствует необходимому условию, т.е. что существует не более ОДНОГО n, удовлетворяющего условию (u ‹= A[n] ‹= i) для любого данного (u,i), который вы предоставляете? Кроме того, это простое совпадение, что ваш входной вектор отсортирован? Не полагаясь на это, разделяй и властвуй в этой проблеме ничего не добьешься, поэтому это важно.   -  person WhozCraig    schedule 08.11.2012
comment
Массив предназначен для сортировки с самого начала. Я просто ищу любой элемент, который соответствует критериям, неважно, 10 их или один. пока кто-то выполняет это, я хочу вернуть истину   -  person Unknown    schedule 08.11.2012
comment
OK. поэтому любое сопоставление значений в целом приведет к истинному значению. какое значение не соответствует этому результату? Если это так, и ввод отсортирован, вы можете получить значительный прирост производительности. и должен иметь возможность сократить время поиска до O (2 * logn)   -  person WhozCraig    schedule 08.11.2012
comment
Это не обязательное условие, нет, это просто то, как я пытаюсь это реализовать. Фактический метод кодирования - это то, что меня сейчас привлекает. Итак, чтобы уточнить, нет, он не ДОЛЖЕН быть первым, просто любой, который соответствует условию, и только один должен соответствовать ему, но могут и другие, мне просто не нужно это указывать.   -  person Unknown    schedule 08.11.2012


Ответы (3)


Предполагая, что ваш входной вектор всегда отсортирован, я думаю, что-то вроде этого может сработать для вас. Это самая простая форма, которую я мог придумать, и производительность O (log n):

bool inRange(int lval, int uval, int ar[], size_t n)
{
    if (0 == n)
        return false;

    size_t mid = n/2;
    if (ar[mid] >= std::min(lval,uval))
    {
        if (ar[mid] <= std::max(lval,uval))
            return true;
        return inRange(lval, uval, ar, mid);
    }
    return inRange(lval, uval, ar+mid+1, n-mid-1);
}

При этом используется подразумеваемое различие диапазонов; т. е. он всегда использует меньшее из двух значений в качестве нижней границы, а большее из двух — в качестве верхней границы. Если ваше использование требует, чтобы входные значения для lval и uval рассматривались как евангелие, и поэтому любой вызов, где lval > uval должен возвращать false (поскольку это невозможно), вы можете удалить расширения std::min() и std::max(). В любом случае вы можете дополнительно повысить производительность, создав внешний фронтальный загрузчик и предварительно проверив порядок lval и uval, чтобы либо (а) немедленно вернуться как false, если требуется абсолютный порядок и lval > uval, либо (б) предварительно определить lval и uval в правильном порядке, если требуется разность диапазонов. Примеры обеих таких внешних оболочек рассматриваются ниже:

// search for any ar[i] such that (lval <= ar[i] <= uval)
//  assumes ar[] is sorted, and (lval <= uval).
bool inRange_(int lval, int uval, int ar[], size_t n)
{
    if (0 == n)
        return false;

    size_t mid = n/2;
    if (ar[mid] >= lval)
    {
        if (ar[mid] <= uval)
            return true;
        return inRange_(lval, uval, ar, mid);
    }
    return inRange_(lval, uval, ar+mid+1, n-mid-1);
}

// use lval and uval as an hard range of [lval,uval].
//  i.e. short-circuit the impossible case of lower-bound
//  being greater than upper-bound.
bool inRangeAbs(int lval, int uval, int ar[], size_t n)
{
    if (lval > uval)
        return false;
    return inRange_(lval, uval, ar, n);
}

// use lval and uval as un-ordered limits. i.e always use either
// [lval,uval] or [uval,lval], depending on their values.
bool inRange(int lval, int uval, int ar[], size_t n)
{
    return inRange_(std::min(lval,uval), std::max(lval,uval), ar, n);
}

Я оставил тот, который, как я думаю, вам нужен, как inRange. Модульные тесты, выполненные для охвата основных и пограничных случаев, приведены ниже вместе с полученным результатом.

#include <iostream>
#include <algorithm>
#include <vector>
#include <iomanip>
#include <iterator>

int main(int argc, char *argv[])
{
    int A[] = {5,10,25,30,50,100,200,500,1000,2000};
    size_t ALen = sizeof(A)/sizeof(A[0]);
    srand((unsigned int)time(NULL));

    // inner boundary tests (should all answer true)
    cout << inRange(5, 25, A, ALen) << endl;
    cout << inRange(1800, 2000, A, ALen) << endl;

    // limit tests (should all answer true)
    cout << inRange(0, 5, A, ALen) << endl;
    cout << inRange(2000, 3000, A, ALen) << endl;

    // midrange tests. (should all answer true)
    cout << inRange(26, 31, A, ALen) << endl;
    cout << inRange(99, 201, A, ALen) << endl;
    cout << inRange(6, 10, A, ALen) << endl;
    cout << inRange(501, 1500, A, ALen) << endl;

    // identity tests. (should all answer true)
    cout << inRange(5, 5, A, ALen) << endl;
    cout << inRange(25, 25, A, ALen) << endl;
    cout << inRange(100, 100, A, ALen) << endl;
    cout << inRange(1000, 1000, A, ALen) << endl;

    // test single-element top-and-bottom cases
    cout << inRange(0,5,A,1) << endl;
    cout << inRange(5,5,A,1) << endl;

    // oo-range tests (should all answer false)
    cout << inRange(1, 4, A, ALen) << endl;
    cout << inRange(2001, 2500, A, ALen) << endl;
    cout << inRange(1, 1, A, 0) << endl;

    // performance on LARGE arrays.
    const size_t N = 2000000;
    cout << "Building array of " << N << " random values." << endl;
    std::vector<int> bigv;
    generate_n(back_inserter(bigv), N, rand);

    // sort the array
    cout << "Sorting array of " << N << " random values." << endl;
    std::sort(bigv.begin(), bigv.end());

    cout << "Running " << N << " identity searches..." << endl;
    for (int i=1;i<N; i++)
        if (!inRange(bigv[i-1],bigv[i],&bigv[0],N))
        {
            cout << "Error: could not find value in range [" << bigv[i-1] << ',' << bigv[i] << "]" << endl;
            break;
        };
    cout << "Finished" << endl;

    return 0;
}

Выходные результаты:

1
1
1
1
1
1
1
1
1
1
1
1
1
1
0
0
0
Sorting array of 2000000 random values.
Running 2000000 identity searches...
Finished
person WhozCraig    schedule 08.11.2012
comment
Поздравляю, вы получили непрерывную рекурсию;) - person bitmask; 08.11.2012
comment
Не ограничивается только @bitmask. Если здесь есть бесконечный случай, я действительно хочу знать, что я пропустил. моя рекурсия немного заржавела, и причина, по которой я также предложил тестовые примеры. - person WhozCraig; 08.11.2012
comment
Извините, я неправильно понял одно условие, это не бесконечная рекурсия, но вы все равно выходите за границы массива для пустых массивов. Незначительная проблема. - person bitmask; 08.11.2012
comment
@bitmask А. Ok. Я действительно хотел, что? Я думал, что учёл их всех. Вы правы, никакого нулевого регистра. должен учитывать это при входе. Спасибо. - person WhozCraig; 08.11.2012
comment
@bitmask спасибо, что поймали случай нулевой длины A[]. Это позволило удалить обе третичные проверки. Очень одобряю. Как я сказал; ржавый = Р - person WhozCraig; 08.11.2012

На самом деле это довольно просто, если вы предполагаете, что массив отсортирован. Вы можете избежать логарифмической сложности, всегда глядя на соответствующую левую или правую часть последовательности:

#include <iterator>

template <typename Limit, typename Iterator>
bool inRange(Limit lowerBound, Limit upperBound, Iterator begin, Iterator end) {
  if (begin == end) // no values => no valid values
    return false;
  Iterator mid = begin;
  auto const dist = std::distance(begin,end);
  std::advance(mid,dist/2); // mid might be equal to begin, if dist == 1
  if (lowerBound < *mid && *mid < upperBound)
    return true;
  if (dist == 1) // if mid is invalid and there is only mid, there is no value
    return false;
  if (*mid > upperBound)
    return inRange(lowerBound, upperBound, begin, mid);
  std::advance(mid,1); // we already know mid is invalid
  return inRange(lowerBound, upperBound, mid, end);
}

Вы можете вызвать это для простых массивов с помощью:

inRange(2,25,std::begin(A),std::end(A));
person bitmask    schedule 08.11.2012
comment
Спасибо за ваш ответ, моему компилятору С++ это не нравится, и он значительно более продвинут, чем то, чем я сейчас занимаюсь. Однако я понимаю основы того, что вы написали, и я посмотрю, что я могу адаптировать для работы со своим, спасибо за ваш ответ! - person Unknown; 08.11.2012
comment
@ArronFitt: вы должны скомпилировать с поддержкой C ++ 11. Если вы используете g++ или clang++, вы можете сделать это, введя -std=c++0x в командной строке. Понятия не имею о вещах с окнами. - person bitmask; 08.11.2012
comment
@bitmask Разве не только использование std::begin и std::end (которое довольно легко имитировать с помощью A и A+sizeof(A)/sizeof(A[0])) требует С++ 11? - person Christian Rau; 08.11.2012
comment
@ChristianRau: Вы имеете в виду помимо ключевого слова auto? - person bitmask; 08.11.2012
comment
@bitmask Ах да, упустил из виду. Хорошо, это не так удобно для имитации. Вместо этого нужно было бы использовать typename std::iterator_traits<Iterator>::difference_type. - person Christian Rau; 08.11.2012

Насколько я понимаю, использование «разделяй и властвуй» для вашей конкретной проблемы не принесет преимущества. Однако, по крайней мере, в вашем примере вход отсортирован; должно быть возможно немного улучшить, пропуская значения, пока не будет достигнута ваша нижняя граница.

person anonymous    schedule 08.11.2012
comment
Ладно, можешь объяснить, как мне это сделать? заранее спасибо - person Unknown; 08.11.2012