2010-12-04 22 views

回答

6
  1. 填補了數組與你的價值觀的範圍
  2. 洗牌陣列
  3. 挑頭5個元素

如果範圍是非常大的,並且希望值的數目是非常小(例如1 ... 1000000範圍內的5個不同值),那麼您可以嘗試在範圍內生成隨機數並丟棄(不太可能)重複,直到您有5個。「繼續嘗試」方法的問題是有一個小小的機會,你可以花費大量的時間用一串隨機值gi給你很多重複的東西。對於大範圍的價值來說,這是不太可能的,除非軟件給創傷患者提供氧氣或者其他東西,否則它可能值得冒險。

+0

非常整齊,我每次都在考慮生成一個隨機值,並將其與當前數字進行比較。但這要快得多。 – Lekensteyn 2010-12-04 13:59:47

+0

@Lekensteyn這一切都取決於範圍的大小和你想要的不同值的數量。關於洗牌技巧的好處在於,它可以保證您不會陷入一個(不太可能,但可能)的循環中,在這個循環中您不斷從「隨機」功能獲取重複。 – Pointy 2010-12-04 14:01:11

+0

真的好主意。然而,我不知道如何在Javascript中洗牌數組,介意給我一個樣本?謝謝。 – user435216 2010-12-04 14:04:25

3

另一種方法是生成隨機數字,並只將它們添加到 返回的數組中,如果它沒有包含它們。

function randomRange(from, to, leng){ 
    var tem, A= [], L= 0, i= 0; 
    randomRangeLoop: 
    while(L< leng){ 
     tem= Math.floor(Math.random()*to)+from; 
     i= 0; 
     while(i<L){ 
      if(A[i++]=== tem) continue randomRangeLoop; 
     } 
     A[L++]= tem; 
    } 
    return A; 
} 

警報(randomRange(1,10,5))

/*返回的值:(陣列) 8,6,1,3,5 */

2
function generateNumbers(resultCount, from, to) { 
    var result = []; // holds the final result 
    var isAlreadyPicked = []; // quick lookup for the picked numbers 
    // holds substitutes for when we pick a number that has been picked before 
    var substitutes = []; 

    for (var i = 0; i < resultCount; i++) { 
    // pick a random number 
    var number = Math.floor(Math.random() * (to - from)) + from; 

    if(isAlreadyPicked[number]) { 
     // pick a number from the ones we skipped at the end of the range. 
     var j = Math.floor(Math.random() * substitutes.length); 
     number = substitutes[j]; 
     substitutes.splice(j, 1); 
    } 

    // Save the number and mark it as being picked. 
    result.push(number); 
    isAlreadyPicked[number] = true; 

    // decrease the range. (Because there is 1 less number to choose from) 
    to -= 1; 

    if(!isAlreadyPicked[to]) { 
     // Save a substitute for when we pick the same number in a next iteration. 
     substitutes.push(to); 
    }  
    } 

    return result; 
} 

它通過每次迭代減少範圍的頂部來工作。如果在前一次迭代中選取的數字已經存在於結果中,只需將其更改爲我們排除在範圍之外的其中一個頂部數字(那裏將始終只有1個未被選中的數字)。這也是一個真正的隨機數,因爲它實際上存在於前面一次迭代中挑選的數字中。

在我看來這是最好的 解決方案,因爲:

  1. 它不通過分配所有的數字數組 和洗牌數組分配大量內存 。

  2. 它只是X迭代所以把它掛 到這個呼吸機:)

  3. 這是真正的隨機,我以前 答案在我加1,如果數量 已經挑不。因爲 之前 迭代中挑選的編號 之後的編號比其他編號獲得挑選的機會多兩倍。

編輯1:我是在大量案件測試此算法和我犯了一個小錯誤。有一個邊緣情況,有時這些數字不是唯一的。發生這種情況時,該範圍內的其中一位頂級號碼已被選中。我更新了我的代碼。

編輯2:如果你有興趣:這裏是我用來測試這個代碼:

function test(cases, size) { 
    var errors = 0; 
    for (var i = 0; i < cases; i++) {  
    var start = Math.floor(Math.random() * size); 
    var end = start + Math.floor(Math.random() * size); 
    var count = Math.floor(Math.random() * (end - start)); 

    var nrs = generateNumbers(count, start, end); 

    console.log('testing', start, end, count, nrs); 

    test: 
    for (var j = 0; j < count; j++) { 
     var testedNumber = nrs[j]; 
     if(testedNumber < start || testedNumber >= end) { 
     console.error('out of range', testedNumber); 
     errors += 1; 
     break test; 
     } 
     for (var k = 0; k < count; k++) { 
     if(j !== k && nrs[k] === testedNumber) { 
      console.error('picked twice', testedNumber); 
      errors += 1; 
      break test; 
     } 
     } 
    } 
    } 

    console.log('all tests finished | errors:', errors) 
} 
test(1000, 20); 

編輯3:尖尖建議使用更快的算法看,如果一個號碼是獨特。我用他/她的建議更新了樣本。謝謝Pointy!

編輯4:通過刪除內部循環並將其替換爲替代數組,使算法更高效一些。有趣的是,這個算法實際上可以用作前面答案中的混洗算法:)

這個問題讓我很感興趣。我以前需要這樣的算法。這實際上是我第一次找到滿足我的解決方案。我對這個主題做了一些研究,這似乎是Fisher-Yates shuffle算法的一個變種。

編輯5:改變算法,從替代品中選擇一個隨機數而不是第一個。

相關問題