簡體   English   中英

在一定范圍內找到N個非重復數字的所有可能組合,這些總數加起來為X

[英]find all possible combinations of N non-repeating numbers within a certain range that add up to X

我幾個月來一直在努力尋找解決方案。 這是我的一個藝術項目。 到目前為止,我可以找到部分的python和c解決方案,但對於我的情況來說它們是沒有用的...我需要使用PHP或Javascript的有效解決方案。

這是問題:

  1. 找到所有可能的N個數字的組合,應滿足以下條件:
    • 數字不能在組合中重復
    • 數字在其他解決方案中不會以不同的順序重復
    • 僅使用整數
  2. 在一定范圍內的整數
  3. 總計X

例如:

  1. 找到3個數字的所有組合
  2. 在1到12之間的所有數字內
  3. 總計15

計算出的解決方案應吐出:

[1,2,12]
[1,3,11]
[1,4,10]
[1,5,9]
[1,6,8]
[1,7,7] = EXAMPLE OF WRONG OUTPUT, NO REPEATING NUMBERS WITHIN COMBINATION
[1,8,6] = EXAMPLE OF WRONG OUTPUT, NO REPEATING NUMBERS IN OTHER SOLUTIONS (see [1,6,8])
[2,3,10]
[2,4,9]
[2,5,8]
[2,6,7]
[3,4,8]
[3,5,7]
[4,5,6]

顯然,手動進行幾分鍾很容易,但是我需要計算更大的范圍和更多的數字,所以我需要一個簡短的腳本來為我做這件事...

任何幫助,將不勝感激!

我覺得處理此挑戰的最優雅方法是通過遞歸。

function getCombos(target, min, max, n) {
    var arrs = [];
    if (n === 1 && target <= max) {
        arrs.push([target]);
    } else {
        for (var i = min; i < target / n && i <= max; i++) {
            var arrays = getCombos(target - i, i + 1, max, n - 1);
            for (var j = 0; j < arrays.length; j++) {
                var array = arrays[j];
                array.splice(0, 0, i);
                arrs.push(array);
            }
        }
    }
    return arrs;
}

說明

通過從最小值i作為每個數組的第一項爬升,然后將其余的( target-i )返回遞歸函數以拆分為n-1分量,每個遞歸的最小值增加一個,從而起作用呼叫。

15 = (1 + 14) = 1 + (2 + 12)
15 = (1 + 14) = 1 + (3 + 11)
15 = (1 + 14) = 1 + (4 + 10)
    ...
15 = (1 + 14) = 1 + (6 + 8)
15 = (2 + 13) = 2 + (3 + 10)
15 = (2 + 13) = 2 + (4 + 9)
    ...
15 = (4 + 11) = 4 + (5 + 6)

請注意,每個數組的第一個索引處的數字永遠不會超過target/n ,其中target是您求和的數字, n是數組中的項目數。 (因此,將15拆分為3個部分時,第一列將始終小於5。)其他列也是如此,但是隨着數組索引的增加, n會減少1。 知道這一點后,我們便可以進行遞歸,而無需在遞歸函數上添加額外的參數。

工作實例

查看下面的代碼片段,以查看實際效果。

 function getCombos(target, min, max, n) { var arrs = []; if (n === 1 && target <= max) { arrs.push([target]); } else { for (var i = min; i < target / n && i <= max; i++) { var nextTarget = target - i; var nextMin = i + 1; var arrays = getCombos(nextTarget, nextMin, max, n - 1); for (var j = 0; j < arrays.length; j++) { var array = arrays[j]; array.splice(0, 0, i); arrs.push(array); } } } return arrs; } document.getElementById("submit").onclick = function () { var target = document.getElementById("target").value; var min = document.getElementById("min").value; var max = document.getElementById("max").value; var n = document.getElementById("n").value; var result = getCombos(+target, +min, +max, +n); document.getElementById("output").innerHTML = result.join("<br/>"); }; 
 .table { display:table; table-layout:fixed; width:100%; } .table-row { display:table-row; } .cell { display:table-cell; } 
 <div class="table"> <div class="table-row"> <div class="cell">Target:</div> <div class="cell"> <input id="target" type="text" value=15> </div> <div class="cell">n:</div> <div class="cell"> <input id="n" type="text" value=3> </div> </div> <div class="table-row"> <div class="cell">Min:</div> <div class="cell"> <input id="min" type="text" value=1> </div> <div class="cell">Max:</div> <div class="cell"> <input id="max" type="text" value=12> </div> </div> </div> <input id="submit" type="button" value="submit" /> <div id="output" /> 

如果以升序生成列表,則將避免兩種重復。

一個簡單的遞歸解決方案包括選擇每個可能的第一個元素,然后遞歸調用生成器以請求可能的延續:即,延續被限制為具有更少的元素,以大於所選元素的值開始,然后求和所需總和減去所選元素。

 Partitions(min, size, total):
   if size is 1:
     if total < min: return nothing
     else return the list [total]

   for each value i between min and total:
     get the set of lists Partitions(i+1, size-1, total-i)
     add i to the beginning of each list
   return all the lists. 

通過不讓i超出最大的實際價值或至少超出保守的估計,可以改善上述情況。 或者,您可以在遞歸調用返回空集后停止i的遞增。

下面是執行所需功能的遞歸函數。

對於您的示例,您可以這樣稱呼它:

combos(3, 1, 12, 15);

附加功能參數( arunningcurrent )跟蹤當前狀態,可以忽略:

var arr= [];

function combos(num, min, max, sum, a, running, current) {
  var i;

  a= a || [];
  running= running || 0;
  current= current || min;

  for(i = current ; i <= max ; i++) {
    if(num===1) {
      if(i+running===sum) {
        arr.push(a.concat(i));
      }
    }
    else {
      combos(num-1, min, max, sum, a.concat(i), i+running, i+1);
    }
  }
};

小提琴

這是一個稍微優化的解決方案。 通過從范圍中的最大到最小進行迭代,跳過所有太大的可能性變得非常容易。

function combos(size, start, end, total, solution) {  
    var solutions = [];
    solution = solution || [];
    if (size === 1) {        
        if (start <= total && end >= total) {            
            solutions.push(solution.concat([total]));
        }
        return solutions;
    } else {
        while (end > start) {
            var newTotal = total - end;                    
            solutions = solutions.concat(
                combos(
                    size - 1, 
                    start, 
                    Math.min(end - 1, newTotal), 
                    newTotal, 
                    solution.concat([end])
                )
            );   
            end--;
        }
        return solutions;
    }
}

對於大量而言,效率可能不高,但是使用3個嵌套的for()循環,您可以做到-

$t=20; // up to X
$s=$t-3; // sets inner loop max
$r=$t/3; // sets middle loop max
$q=$r-1; // sets outer loop max
$results= array(); // array to hold results

for($x=1;$x<=$q;$x++){

    for($y=($x+1);$y<=$r;$y++){

        for($z=($x+2);$z<=$s;$z++){

            // if sum == max && none are the same value
            if(($x+$y+$z)==$t && ($x!=$y && $x!=$z && $y!=$z)){
                $results[]=array($x,$y,$z);

            }

        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM