4

Если у меня есть массив чисел и список сумм, суммирующих элементы массива, то какой наиболее эффективный подход (или, по крайней мере, не грубой взлом) для определения того, какой из элементов включены в сумму?сопоставлять элементы со списком сумм

Упрощенный пример может выглядеть следующим образом:

массива = [6, 5, 7, 8, 6, 12, 16] суммирует = [14, 24, 22]

, и я хотел бы чтобы узнать:

14 включает в себя 8, 6

24 включает в себя 5, 7, 12

22 включает в себя 6, 16

function matchElements(arr, sums) { 
    var testArr; 
    function getSumHash() { 
     var hash = {}, 
      i; 
     for (i = 0; i < sums.length; i++) { 
      hash[sums[i]] = []; 
     } 
     return hash; 
    } 
    sums = getSumHash(); 
    // I don't have a good sense of where to start on what goes here... 
    return sumHash; 
} 

var totals = matchElements([6, 5, 7, 8, 6, 12, 16], [14,24,22]), 
    total; 

for (total in totals) { 
    console.log(total + "includes", totals[total]) 
} 

http://jsfiddle.net/tTMvP/

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

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

+0

Хотя я мог использовать «грубую силу», как вы выразились, я не знаю ни одного «быстрого» алгоритма для его достижения. – Xotic750

+0

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

+0

http://www.roryhart.net/code/xkcd-np-complete-restaurant-order/ Вот кто-то, кто решил проблему, к сожалению, я ничего не знаю о Minizinc, видимо, это касается проблем моделирования в «Программе ограничения», , Так что это интересно, но не полезно. – Shane

ответ

2

Ok , проклинаю меня, это мой нокаут, улучшения приветствуются :)

Я считаю, что это Bin Packing Problem или knapsack problem

Javascript

Общие Power Set функция

function powerSet(array) { 
    var lastElement, 
     sets; 

    if (!array.length) { 
     sets = [[]]; 
    } else { 
     lastElement = array.pop(); 
     sets = powerSet(array).reduce(function (previous, element) { 
      previous.push(element); 
      element = element.slice(); 
      element.push(lastElement); 
      previous.push(element); 

      return previous; 
     }, []); 
    } 

    return sets; 
} 

уменьшает копии в наборе мощности, то есть мы не хотим [6, 8] и [8, 6] они же

function reducer1(set) { 
    set.sort(function (a, b) { 
     return a - b; 
    }); 

    return this[set] ? false : (this[set] = true); 
} 

Основная функция, получает соответствие для бункера, удаляет использованные предметы, ополаскивает и повторить

function calc(bins, items) { 
    var result = { 
      unfilled: bins.slice(), 
      unused: items.slice() 
     }, 
     match, 
     bin, 
     index; 

    function reducer2(prev, set) { 
     if (!prev) { 
      set.length && set.reduce(function (acc, cur) { 
       acc += cur; 

       return acc; 
      }, 0) === bin && (prev = set); 
     } 

     return prev; 
    } 

    function remove(item) { 
     result.unused.splice(result.unused.indexOf(item), 1); 
    } 

    for (index = result.unfilled.length - 1; index >= 0; index -= 1) { 
     bin = result.unfilled[index]; 
     match = powerSet(result.unused.slice()).filter(reducer1, {}).reduce(reducer2, ''); 
     if (match) { 
      result[bin] = match; 
      match.forEach(remove); 
      result.unfilled.splice(result.unfilled.lastIndexOf(bin), 1); 
     } 
    } 

    return result; 
} 

Это наши изделия и бункеры они должны быть упакованы в

var array = [6, 5, 7, 8, 6, 12, 16], 
    sums = [14, 24, 22]; 

console.log(JSON.stringify(calc(sums, array))); 

Выход

{"14":[6,8],"22":[6,16],"24":[5,7,12],"unfilled":[],"unused":[]} 

На jsfiddle

+0

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

+0

Спасибо, но это все еще очень «грубая сила». Я уверен, что кто-то может сделать намного лучше. ;) – Xotic750

+0

Поскольку это «np-complete», как упомянуто Викрамом Бхатом, лучшее, что вы можете сделать, - это угадать. Чем больше я смотрел на это, тем больше я понял, что у меня нет выбора, кроме как снизить ожидания. На данный момент я просто хочу что-нибудь, что может дать результат, поскольку это официально является нетривиальной проблемой. – Shane

1

Проблема подмножестве суммы является NP-полной, но есть псевдополиномиальный алгоритм динамического программирования решение: -

1.calculate the max element of the sums array 
2. Solve it using knapsack analogy 
3. consider knapsack capacity = sums[max] 
4. items as arr[i] with weight and cost same. 
5. maximize profit 
6. Check whether a sum can be formed from sums using CostMatrix[sums[i]][arr.length-1]==sums[i] 

Вот реализация Java того же: -

public class SubSetSum { 
    static int[][] costs; 

    public static void calSets(int target,int[] arr) { 

     costs = new int[arr.length][target+1]; 
     for(int j=0;j<=target;j++) { 
      if(arr[0]<=j) { 

       costs[0][j] = arr[0]; 
      } 
     } 
     for(int i=1;i<arr.length;i++) { 

      for(int j=0;j<=target;j++) { 
       costs[i][j] = costs[i-1][j]; 
       if(arr[i]<=j) { 
        costs[i][j] = Math.max(costs[i][j],costs[i-1][j-arr[i]]+arr[i]); 
       } 
      } 

     } 

     // System.out.println(costs[arr.length-1][target]); 
     /*if(costs[arr.length-1][target]==target) { 
      //System.out.println("Sets :"); 
      //printSets(arr,arr.length-1,target,""); 
     } 

     else System.out.println("No such Set found");*/ 

    } 

    public static void getSubSetSums(int[] arr,int[] sums) { 

     int max = -1; 
     for(int i=0;i<sums.length;i++) { 
      if(max<sums[i]) { 
       max = sums[i]; 
      } 
     } 

     calSets(max, arr); 

     for(int i=0;i<sums.length;i++) { 
      if(costs[arr.length-1][sums[i]]==sums[i]) { 
       System.out.println("subset forming "+sums[i]+":"); 
       printSets(arr,arr.length-1,sums[i],""); 
      } 
     } 




    } 

    public static void printSets(int[] arr,int n,int w,String result) { 


     if(w==0) { 
      System.out.println(result); 
      return; 
     } 

     if(n==0) { 
      System.out.println(result+","+arr[0]); 
      return; 
     } 

     if(costs[n-1][w]==costs[n][w]) { 
      printSets(arr,n-1,w,new String(result)); 
     } 
     if(arr[n]<=w&&(costs[n-1][w-arr[n]]+arr[n])==costs[n][w]) { 
      printSets(arr,n-1,w-arr[n],result+","+arr[n]); 
     } 
    } 

    public static void main(String[] args) { 
     int[] arr = {6, 5, 7, 8, 6, 12, 16}; 
     int[] sums = {14, 24, 22};   
     getSubSetSums(arr, sums); 

    } 
} 
+0

Мне потребуется некоторое время, чтобы успеть, поскольку я никогда не касался java и не слышал о программировании ограничения до сегодняшнего вечера :) +1 сейчас, несмотря на то, что я пробираюсь через это. Благодаря! – Shane

+0

Я принял решение Xotic750 над этим просто потому, что он был реализован в javascript. Ваш пост помог углубить мое понимание проблемной области, поскольку это было моим первым введением в реальном мире. Хорошая информация – Shane

2

Возможно, было бы поучительно показать, как это можно закодировать в системе программирования ограничений (здесь MiniZinc).

Вот полная модель. Она также доступна на http://www.hakank.org/minizinc/matching_sums.mzn

int: n; 
int: num_sums; 
array[1..n] of int: nums; % the numbers 
array[1..num_sums] of int: sums; % the sums 

% decision variables 

% 0/1 matrix where 1 indicates that number nums[j] is used 
% for the sum sums[i]. 
array[1..num_sums, 1..n] of var 0..1: x; 

solve satisfy; 

% Get the numbers to use for each sum 
constraint 
    forall(i in 1..num_sums) (
     sum([x[i,j]*nums[j] | j in 1..n]) = sums[i] 
    ) 
; 

output 
[ 
    show(sums[i]) ++ ": " ++ show([nums[j] | j in 1..n where fix(x[i,j])=1]) ++ "\n" 
    | i in 1..num_sums 
]; 

%% Data 
n = 6; 
num_sums = 3; 
nums = [5, 7, 8, 6, 12, 16]; 
sums = [14, 24, 22]; 

Матрица «х» интересная часть, х [I, J] = 1 (истина), если число «НУМС [J]» используется в сумме число «суммы [i]».

Для этой конкретной проблемы есть 16 решений:

.... 
14: [8, 6] 
24: [8, 16] 
22: [6, 16] 
---------- 
14: [6, 8] 
24: [6, 5, 7, 6] 
22: [6, 16] 
---------- 
14: [6, 8] 
4: [5, 7, 12] 
22: [6, 16] 
---------- 
14: [6, 8] 
24: [6, 6, 12] 
22: [6, 16] 
---------- 
14: [6, 8] 
24: [8, 16] 
22: [6, 16] 
---------- 
... 

Они являются не отдельными решениями, так как есть два 6-х. С помощью всего лишь один 6 есть 2 решения:

14: [8, 6] 
24: [5, 7, 12] 
22: [6, 16] 
---------- 
14: [8, 6] 
24: [8, 16] 
22: [6, 16] 
---------- 

Помимо: Когда я впервые прочитал эту проблему, я не был уверен, что если цель заключается в минимизации (или максимальный) числа используются. С некоторыми дополнительными переменными и ограничениями модель также может быть использована для этого.Вот решение, которое использует наименьшее количество цифр:

s: {6, 8, 16} 
14: [8, 6] 
24: [8, 16] 
22: [6, 16] 
Not used: {5, 7, 12} 

И наоборот, максимальное количество чисел, используемых (здесь все номера используются с 6 подсчитывают только один раз в «с»):

s: {5, 6, 7, 8, 12, 16} 
14: [8, 6] 
24: [5, 7, 12] 
22: [6, 16] 
Not used: {} 

Расширенная модель MiniZinc доступна здесь: http://www.hakank.org/minizinc/matching_sums2.mzn.

(примечание 2: В комментарии упоминается проблема с рестораном xkcd. Вот более общее решение для этой проблемы: http://www.hakank.org/minizinc/xkcd.mzn. Это вариант текущей проблемы соответствия, основное отличие состоит в том, что блюдо можно пересчитать более одного раза, а не только 0..1, как в этой проблеме совпадения.)

+0

Я был намеренно расплывчатым в первоначальном требовании, потому что мой фокус довольно узкий, и я подумал, что лучше спросить широкий вопрос, чтобы принести пользу другим. Я фактически взламываю скрипт, чтобы сопоставить продажи с депозитами от разбора нескольких файлов csv. Переход к рекурсии через диапазоны дат и поиск правильных комбинаций для помощи в согласовании, чтобы компенсировать некоторые безвозвратные записи. Как вы можете себе представить, угадывание вручную занимает много времени. :) – Shane

+1

Да, я заметил разъяснение, но я думал, что вариант оптимизации был довольно аккуратным. :-) Кстати, есть хотя бы один пакет Javascript, который интегрируется с системой программирования ограничений: http://code.google.com/p/fdcp/, которые используют Gecode. Тем не менее, я не тестировал его сам (это в моем списке дел). – hakank

+1

невероятно полезен в ссылке на библиотеку ... Чтобы не загромождать комментарии слишком плохо, я просто скажу, что я буду изучать ваш ответ позже, поскольку я из глубины дела нахожусь на этом, очень благодарен. :) – Shane