2011-06-01 28 views
8

有人能告訴我從無序數組中找出最大整數的最佳方法嗎?來自未分類數組總和的PHP最大整數

例如

{0.1, 0.2, 0.9, 0.5} 

Largest whole number possible is 1 (0.1 + 0.9). 

{0.9, 0.2, 0.5, 0.3, 0.9} 

Largest possible is 2 (0.9 + 0.9 + 0.2) 

感謝

更新

我已經接受,我用的方法,但一些下面將編程正確

+5

嗯我認爲這是與knap-sack問題有關:http://en.wikipedia.org/wiki/Knapsack_problem然而,可能有更好的解決方案,因爲你比「0-1揹包問題「。 – 2011-06-01 18:29:40

+2

@Matt如果該集合沒有任何可能的整數和,那該怎麼辦?return 0?即「{0.1,0.1,0.1,0.2}' – Fosco 2011-06-01 18:32:53

+0

@Matt或{0.7,0.7,0.7}?沒有完整的數據可以從中得到,那麼你期望的結果是什麼? – Fosco 2011-06-01 18:35:18

回答

3

我會建議總結整個數組,然後找到與小數部分相等的最小總和。除非數字在小數點後具有非常高的精度,否則無論採用哪種方法來找到確切的數字,這種反轉都會節省大量的計算量。

此外,對數組進行排序並從最小的數字開始貪婪可能會產生良好的結果。但是,最佳解決方案很大程度上取決於初始集合的性質。你能提供關於你期望的數字的更接近的規格嗎?

+0

+1這聽起來很值得調查! – Fosco 2011-06-03 13:37:35

+0

感謝大衛,這與我的目標類似。說實話,準確性不是問題,但會很好。所以,我總結了數組,然後對其進行升序排序,並從總和中獲取數組中的第一個數字,並檢查它是否爲int。它不會總是讓我獲得最高的數字,但足以滿足我的需求。謝謝 – Matt 2011-06-03 14:08:08

0

這樣的事情?

$array=array(0.1, 0.2, 0.9, 0.5); 
arsort($array); 
while($highest=array_shift($array)){ 
    foreach($array as $val){ 
    $thisval=($highest+val); 
    if(round($thisval)==($thisval)){ 
    return $thisval; 
    } 
} 
} 

編輯:@Felix克林你是絕對正確的,添加了一個while循環

+1

那麼{0.1,0.3,0.7,0.8}呢?你的代碼不會工作,然後... – 2011-06-01 18:30:30

+0

?我不熟悉語法: -/ – Trey 2011-06-01 18:32:20

+1

@trey:我只是說,如果你有這些值呢?您的代碼只與其他所有代碼的總和最高。在這種情況下,產生一個整數的總和是'0.7'和'0.3',但是你從不測試這些。 – 2011-06-01 18:33:20

0

我沒有寫這個代碼,但僞代碼如下。一般的想法是,你計算組合(x選擇y ... http://en.wikipedia.org/wiki/Combination),然後求和每個組合。你迭代這個數組的長度,然後把你的最大值。

我也確信有關於貪婪和短路這個循環的優化。

$len = count($decimals); 
$maxVals = array(); 

for($choose = $len; $choose > 1; $choose--) { 
    // get $decimals choose $choose 

    // loop and sum each choose array, checking for an integer sum 

    // store max if exists 
} 

return sum($maxVals); 
0

一下OK思考。

$sum = array_sum($arr); 
$floor_sum = floor($sum); 
if ($sum = $floor_sum){ 
return $sum 
}else{ 

for ($i = 0; $i < sizeof($arr); $i++){ 
    if ($sum - $arr[$i] = $floor_sum){ 
    return $sum - $arr[i]; 
    }else{ 
    for ($x = 0; $x < sizeof($arr)- 1; $x++){ 
     if ($x != $i){ 
     if ($sum - $arr[$i] - $arr[$x] = $floor_sum){ 
     return $sum - $arr[$i] - $arr[$x]; 
     }else { 
      //Iterate more times 
     } 
     } 
    } 
    } 
} 

} 

我有上面的但必須有一個更簡單的方法來做到這一點?

+0

該死的,這個假設$ floor_sum實際上是可能的 – Matt 2011-06-01 19:11:23

+0

我想我可以從$ floor_sum減1然後重新開始......任何人都有一個BlueGene free ? – Matt 2011-06-01 19:12:27

1

該代碼的大部分用於獲取數組的排列。我確信它可以被優化,但是這是在四核心單Xeon服務器上計算45個長度爲4,5和6的3個陣列。當我添加第7個小數點的第4個數組時跳到大約220ms,如果我用第8個數添加第5個數列,跳到大約220ms。

基本上,所有這些都是獲取包含浮點數的所有數組的排列,一個按鍵一起按鍵,如果總和是一個大於當前整數的整數,則更新該數字。最終返回儘可能大的數字。

$set1 = array(0.1, 0.2, 0.9, 0.5); 
$set2 = array(0.9, 0.2, 0.5, 0.3, 0.9); 
$set3 = array(0.9, 0.2, 0.5, 0.3, 0.9, 0.4); 

function largestWholeNumber($decimals){ 
    echo "Calculating largest whole number for decimal set '" 
    . implode(",", $decimals) 
    . "'<br />"; 
    $perms = perms($decimals); 
    $answer = 0; 
    foreach($perms as $dec_array){ 
     $current_guess = 0; 
     foreach($dec_array as $dec){ 
      $current_guess += $dec; 
      if (!preg_match("/[^0-9]+/", $current_guess)){ 
       if ($answer < $current_guess){ 
        $answer = $current_guess; 
        echo "New whole number found " 
        ."'$answer'" 
        ." with permutation: <br />"; 
        print_r($dec_array); 
        echo "<br />"; 
       } 
      } 
     } 
    } 
    echo "Result: $answer<br /><br />"; 
    return $answer; 
} 

function factorial($int){ 
if($int < 2) { 
     return 1; 
} 

for($f = 2; $int-1 > 1; $f *= $int--); 

return $f; 
} 


function perms($arr) { 
    $p = array(); 
    for ($i=0; $i < factorial(count($arr)); $i++) { 
     $p[] = perm($arr, $i); 
    } 
    return $p; 
} 


function perm($arr, $nth = null) { 

    if ($nth === null) { 
     return perms($arr); 
    } 

    $result = array(); 
    $length = count($arr); 

    while ($length--) { 
     $f = factorial($length); 
     $p = floor($nth/$f); 
     $result[] = $arr[$p]; 
     array_delete_by_key($arr, $p); 
     $nth -= $p * $f; 
    } 

    $result = array_merge($result,$arr); 
    return $result; 
} 
function array_delete_by_key(&$array, $delete_key, $use_old_keys = FALSE) { 

    unset($array[$delete_key]); 

    if(!$use_old_keys) { 
     $array = array_values($array); 
    } 

    return TRUE; 
} 

largestWholeNumber($set1); // 1 
largestWholeNumber($set2); // 2 
largestWholeNumber($set3); // 3 

信貸的陣列排列函數去dirk dot avery a t gmailhttp://php.net/manual/en/function.shuffle.php

+0

添加了回聲,以便您可以看到它是如何得出答案的。 – 2011-06-01 20:01:28

0

這是思考一個很大的問題。關閉我的頭頂,這是我使用的僞代碼:

  1. A =元素數組。
  2. A'=排序的元素數組。
  3. S =所有元素的總和。
  4. 雖然A '不爲空:
    1. V = S. S = S的
    2. R =餘數的整數部分 - 地板(S)
    3. 使一個的臨時副本',X。
    4. 迭代從最大到作爲X X的最小值[I]:
      1. 如果X [I] < R,減去X [I]從R.移去X [I]從X.
      2. 若R == 0,我們找到了解決方案。 V是整數,X包含加數。停。
    5. 如果X爲空,則沒有解決方案。停。
    6. 將A減小到最大值。 S = S - Max(A')。從A'刪除最大值(A')。
  5. 返回步驟#4。

當然,我不得不看看這是否真的有效。這裏是(很凌亂,被扔掉的質量)的代碼,我寫來測試它:

<?php 
$AA = $A = array(0.1, 0.2, 0.9, 0.5); 

bcscale(8); 

sort($AA, SORT_NUMERIC); 
echo 'A = ' . implode(', ', $A), PHP_EOL; 
echo 'A\' = ' . implode(', ', $AA), PHP_EOL; 

$S = array_sum($AA); 
echo 'S = ' . $S, PHP_EOL; 

while (count($AA)) { 
    $V = floor($S); 
    echo 'V = ' . $V, PHP_EOL; 

    $R = bcsub($S, $V); 
    echo 'R = ' . $R, PHP_EOL; 

    $X = $AA; 
    $XX = array(); 

    // Look for the largest value that is less than or equal to R. 
    for ($i = count($X) - 1; $i >= 0; $i--) { 
     echo 'X[i] = ' . $X[$i] . ', R = ' . $R, PHP_EOL; 
     $c = bccomp($X[$i], $R); 
     if ($c > 0) { 
      continue; 
     } 
     $XX[] = $X[$i]; 
     $R = bcsub($R, $X[$i]); 
     unset($X[$i]); 

     if (bccomp($R, '0', strlen($R)) == 0) { 
      echo 'Largest whole number sum: ' . $V, PHP_EOL; 
      echo 'Elements: ' . implode(', ', $X), PHP_EOL; 
      break 2; 
     } 
    } 

    if (count($X) == 0) { 
     echo 'No sums to a whole number are possible.', PHP_EOL; 
     break; 
    } 

    $t = array_pop($AA); 
    $S = bcsub($S, $t); 
} 

echo 'S = ' . $S, PHP_EOL; 
?> 

這是一個醜陋的O(N^2)算法,但它應該是正確的。任何人都可以看到這將失敗的初始起始數組?

爲了好玩,我試着用50個元素的數組,這些線替換第一行:

$A = array(); 
for ($i = 0; $i < 50; $i++) { 
    $A[] = mt_rand(1, 99)/100.0; 
} 
$AA = $A; 

一目瞭然,它看起來正確 - 我會離開驗證了別人;)