Рекурсивный возврат - PullRequest
       45

Рекурсивный возврат

5 голосов
/ 23 февраля 2011

У меня проблема с моей функцией возврата, она зацикливается на определенных данных. Я не могу написать здесь весь программный код, но могу поставить здесь свою функцию.

bool shareMoney(int quantity, int *values, int index, int moneyA, int half, bool *ifChosen)
{

    if(moneyA == half)
        return true;
    else if(index >= quantity)
        return false;

    moneyA += values[index];

    if(shareMoney(quantity, values,index+1, moneyA, half, ifChosen))
    {
        ifChosen[index] = true;
        return true;
    };

    moneyA -= values[index];

    if(shareMoney(quantity, values,index+1, moneyA, half, ifChosen))
    {
        ifChosen[index] = false;
        return true;
    };

    return false;
}

Теперь вот объяснение:

количество - количество элементов в массиве значений
значения - массив чисел
index - переменная для управления рекурсией
moneyA - переменная, в которой хранится сумма элементов из значений массива
half- число, которое moneyA должно достигнуть после выполнения рекурсии
ifChosen - массив логических элементов, ссылающихся на значения массива

Функция получает количество элементов, равное длине массива значений, значения массива с числами в немотсортированный от самого высокого к низшему, индекс управляет рекурсией и по умолчанию равен 0, поэтому он начинается с первого элемента, переменной moneyA, которая хранит числа из массива значений и должна достигать половины, равной половине чисел, суммируемых из массива значений и ifChosenхранит выбранные номера.

WЭто делает дырочная функция, она суммирует элементы из массива значений и проверяет, достигла ли она половины, если она меньше половины, добавляет ее к moneyA и помечает в ifChosen, затем переходит к следующему, если сумма больше половины, которую она получаетвернуться и снять отметку в массиве ifChosen и вычесть из moneyA.Он всегда должен получать самые высокие элементы.

Вот простой пример:

6 - number of elements
50, 20, 19, 15, 2, 2 - elements stored in values array
total sum is - 108
half of elements - 54

Результат для этого должен быть:

50, 2, 2 - marked as true in ifChosen
20, 19, 15 - marked as false in ifChosen 

Конечно, для этогоПростой пример, он делает большую работу, но для более сложных, которые имеют больше чисел, и одно число встречается более одного раза, оно повторяется и рекурсия никогда не останавливается.Я фактически работал над этим в течение 1,5 недель и спросил всех своих друзей, но никто не знает, что с ним не так.Я знаю, что это имеет какое-то отношение к проблеме с рюкзаком, но у меня ее еще не было, и я все еще должен учиться.

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

Извините за пунктуацию, но я здесь впервые и не привык к форматированию.

Вот один пример:

89 86 83 67 53 45 5 1    

44 43 34 33 33 24 23 23 23 22 21 21 19 12 11 9 8 7 5 3 2 2 2 1 1 1 1 1     

real    0m28.007s    

user    0m27.926s    

sys 0m0.028s

Теперь, по-моему, одна петля навсегда: 43 элемента:

12 2 2 1 3 4 5 6 7 89 33 12 45 23 44 23 11 44 1 3 5 7 88 33 8 19 43 22 86 5 34 23 21 67 83 24 21 53 9 11 34 1 1

@ Карл Билефельдт Да, я знаю, что существует так много комбинаций, поэтому я пытаюсь ускорить их.Пока это все, что я получил, но это дает мне неверные результаты для определенного ввода.Кто-нибудь может сделать это правильно, он работает намного быстрее, чем раньше?

bool shareMoney(int quantity, int *values, int index, int moneyA, int half, bool *ifChosen){

if(index>=quantity && moneyA == half){ return true;}
else if(index>=quantity) return false;

moneyA+=values[index];
ifChosen[index]=true;

if(moneyA<=half){       
    shareMoney(quantity,values,index+1,moneyA, half,ifChosen);
    if(moneyA==half) return true;

    return true;
}else{
    shareMoney(quantity,values,index+1,moneyA, half,ifChosen);      
    moneyA-=values[index];
    ifChosen[index]=false;

    return true;
}


return false;}

Ответы [ 2 ]

2 голосов
/ 24 февраля 2011

Типичный способ сократить количество итераций для такой задачи - вычислить границу для поддерева, решая линейную программу (как и ваша задача, но оставшиеся переменные могут принимать дробные значения).Simplex решает линейную программу за приблизительно квадратичное время вместо экспоненциального.Лучшее решение для линейной программы, по крайней мере, так же хорошо, как и лучшее целочисленное или двоичное решение с теми же ограничениями, поэтому, если линейное решение хуже вашего текущего лучшего, вы можете отбросить целое поддерево без исчерпывающей оценки.

РЕДАКТИРОВАТЬ: Давайте начнем с упрощения алгоритма грубой силы:

int* shareMoney( int pool_size, int *pool, int *solution, int cumsum, int goal)
{
    if (cumsum == goal) return solution;
#if PRUNE_ABOVE
    if (cumsum > goal) return 0;
#endif
    if (!pool_size) return 0;

#if PRUNE_BELOW
    int max = cumsum;
    for( int n = pool_size; n--; max += pool[n] );
    if (max < goal) return 0;
#endif

    int* subproblem = shareMoney(pool_size-1, pool+1, solution, cumsum, goal);
    if (subproblem) return subproblem;

    *solution = *pool;
    return shareMoney(pool_size-1, pool+1, solution+1, cumsum+*pool, goal);
}

После выполнения, solution содержит список значений, используемых для достижения цели, а возвращенный указатель указывает конецlist.

Условные блоки - мое первое предлагаемое улучшение.В этих случаях рекурсия не требуется.

Мы можем исключить необходимость повторения для вычисления max на каждом шаге:

int* shareMoney( int pool_size, int *pool, int *solution, int cumsum, int poolsum, int goal)
{
    if (cumsum == goal) return solution;
#if PRUNE_ABOVE
    if (cumsum > goal) return 0;
#endif
    if (!pool_size) return 0;

#if PRUNE_BELOW
    if (cumsum + poolsum < goal) return 0;
#endif

    int* subproblem = shareMoney(pool_size-1, pool+1, solution, cumsum, poolsum - *pool, goal);
    if (subproblem) return subproblem;

    *solution = *pool;
    return shareMoney(pool_size-1, pool+1, solution+1, cumsum+*pool, poolsum - *pool, goal);
}

Вот функция для решения целочисленной версии (лучше для повторных монетДеноминации):

int* shareMoney( int pool_size, int *pool_denom, int *pool_cardinality, int *solution, int cumsum, int poolsum, int goal)
{
    if (cumsum == goal) return solution;
#if PRUNE_ABOVE
    if (cumsum > goal) return 0;
#endif
    if (!pool_size) return 0;

#if PRUNE_BELOW
    if (cumsum + poolsum < goal) return 0;
#endif

    poolsum -= *pool_cardinality * *pool_denom;
    for (*solution = *pool_cardinality; *solution >= 0; --*solution) {
        int* subproblem = shareMoney(pool_size-1, pool_denom+1, pool_cardinality+1, solution+1, cumsum + *solution * *pool_denom, poolsum, goal);
        if (subproblem) return subproblem;
    }

    return 0;
}

Вместо того, чтобы получить прямой список отдельных монет, он принимает список номиналов и количество доступных монет каждой.Результатом является количество монет каждого номинала, необходимое для решения.

0 голосов
/ 24 февраля 2011

Для 43 элементов существует около 9 триллионов возможных комбинаций.Нет никакого способа ускорить это, если вам нужно проверить все 9 триллионов, но в случае, если вы не хотите ждать так долго, уловка состоит в том, чтобы попытаться приблизить ответ к началу цикла.Я думаю, что вы, вероятно, нашли правильное решение, отсортировав его в порядке возрастания.Вероятно, это быстрее, потому что сначала организуются большие фрагменты (потому что вы выполняете рекурсию в глубину).

Если я правильно понимаю проблему, то найдется комбинация наименьших элементов, составляющая ровно половинуобщая стоимость.Это означает, что элементы, которые не являются выбранными , а также , должны составлять ровно половину общего значения и будут самыми большими элементами.

...