2015-11-06 42 views
-1

我試圖在1-10之間生成5個沒有重複值的隨機數字。因此,我創建了一個遞歸方法,該方法應該檢查是否已經爲陣列中的任何位置創建了值。如果有,則會創建一個新的隨機數並再次檢查。如何使用遞歸方法在C#中創建隨機唯一編號?

這裏是我的代碼:

static Random randomObject = new Random(); 

    static void Main(string[] args) 
    { 

    long[] randomArr = new long[5]; 


    for (int i = 0; i < randomArr.Length; i++) 
    { 
     if (randomArr[i] == randomArr[0]) 
     { 
      randomArr[i] = randomObject.Next(1, 11); 
     } 
     else 
     { 
      long check = randomObject.Next(1, 11); 
      randomArr[i] = CheckIfUnique(check, randomArr); 
     } 
    } 

    Console.WriteLine("\nPress the [enter] key to continue..."); 
    Console.ReadLine(); 

} 

static long CheckIfUnique(long a, long[] b) 
{ 
    for (int i = 0; i <= b.GetUpperBound(0); i++) 
    { 
     if (a == b[i]) 
     { 
      a = randomObject.Next(1, 11); 
      CheckIfUnique(a, b); 
     } 
    } 
    return a; 
} 

但我仍然得到重複的值。有誰知道我的邏輯中是否有錯誤,或者編譯器會在執行如此多的遞歸步驟後出現錯誤?

+6

在CheckIfUnique中,您不返回CheckIfUnique(a,b),您只需調用它。那是你要的嗎? – ergonaut

+5

你是否在使用遞歸,因爲你*有*(作爲測試的一部分,也許),或者因爲你認爲這是必要的?這真的不是解決這個問題的適當方法。 –

+3

如果你是在1到10之間的值之後,你爲什麼使用long? –

回答

3

注 - 這是不是一個遞推答案


您可以更加容易真正做到:

static Random randomObject = new Random(); 

static void Main(string[] args) 
{ 
    long[] randomArr = new long[5]; 

    for (int i = 0; i < randomArr.Length;) 
    { 
     long t = randomObject.Next(1, 11); 
     if(CheckIfUnique(t, randomArr, i)) 
     { 
       randomArr[i++] = t; 
     } 
    } 
} 

static bool CheckIfUnique(long a, long[] b, int length) 
{ 
    for (int i = 0; i < length; i++) 
    { 
     if (a == b[i]) 
     { 
      return false 
     } 
    } 
    return true; 
} 

這樣一來,你的循環,直到你有足夠的隨機值,但是一旦產生了新的獨特價值,你就只會推進櫃檯。

+1

'CheckIfUnique'幾乎可以完全減少到'return!b.Contains(a);' –

+0

@RonBeyer - 當然。這些代碼中的大部分可以被重構爲更簡單更好的東西,但我試圖保持與原來的接近。 – Amit

+0

@Amit,謝謝。這正是我所期待的。有意義而不必遞歸。 –

2

你的方法確實很重。作爲一個變體,你可以看到這種方法。但是,如果你想要,我可以嘗試糾正你的算法。數字的順序是由Random類的原創:

HashSet<int> origNumbers = new HashSet<int>(); 
Random rnd=new Random();    
do 
{ 
    int k = rnd.Next(10); 
    origNumbers.Add(k); 
} 
while (origNumbers.Count <5); 

更新(感謝@AlexeiLevenkov):

如果添加元素的順序很重要,最好是使用收集SortedSet<T>。通過Random類創建號碼的順序按升序排序,它不是由Random類創建的順序:

SortedSet<int> origNumbers = new SortedSet<int>(); 
Random rnd=new Random();    
do 
{ 
    int k = rnd.Next(10); 
    origNumbers.Add(k); 
} 
while (origNumbers.Count <5); 
+1

因爲'HashSet'中元素的順序沒有定義,所以你不應該存儲順序很重要的項目列表(即,符合HashSet實現甚至可以按照順序返回項目)。 http://stackoverflow.com/questions/657263/does-hashset-preserve-insertion-order –

+0

@AlexeiLevenkov我有沒有發現你的想法?請看我最新的答案。 – StepUp

+0

不,你期望結果如何?更新後的代碼收集可能重複的5個隨機數的排序列表......您可能想要在回答中清楚地重述問題,例如「此代碼將提供.....」以闡明您正在回答的問題。原始樣本是合理的(肯定比目前接受的要快),甚至可能是OP所需要的 - 但是它具有未定義的關於項目順序的行爲 - 如果你想保留訂單使用列表(或數組,鏈表)。 –

1

這裏是一個非常簡單的解決辦法:

var random = new System.Random(Guid.NewGuid().GetHashCode()); 
var values = Enumerable.Range(0, 10).OrderBy(x => random.Next()).Take(5).ToArray(); 
+0

這是否產生一個不帶偏見的排列? 「比較」函數並不穩定(對於任何給定的A和B,它會產生一個隨機排序),所以你可能期望更大的數字更可能在集合中間結束,因爲它們是在稍後添加的。 –

+0

@DanBryant這是* *其實是偏頗的結果,但那是因爲'OrderBy' *爲*的狀態排序,所以任何時候用於不同條目的隨機數發生碰撞較早進入永遠是第一位,這樣的項目是早原始結果可能會在最終結果中保持較早,而不是均勻分佈。 – Servy

+0

@Servy,我覺得比這更麻煩一點。使用特定Quicksort對於「良好行爲」的比較函數是穩定的,但我不確定它在兩個元素的比較總是返回新的隨機值時的行爲如何,而不管這兩個元素是否先前進行了比較。我希望至少有一個小偏見(由於你提到的原因),但我懷疑它實際上比這更糟糕。OrderBy使用插入排序會更清楚,因爲那會更明顯地將後面的元素偏向該系列的前面。 –

0

這不是遞歸,但除非是家庭作業,遞歸不會爲你的情況

工作非常好。在這種情況下,以下假設:

1)沒有重複

2)相對小範圍的可能值(幾百最多)

我將填充你的所有可能的值小的初始數據集,然後從這些選擇:

static Random randomObject = new Random(); 
    static void Main(string[] args) 
    { 
     int MAX_NUMBER = 10; 
     int MIN_NUMBER = 1; 
     int NUMBER_OF_RESULTS_REQUIRED = 5; 

     long[] randomArr = new long[NUMBER_OF_RESULTS_REQUIRED]; 
     //Use a pool of all possible values 
     List<long> dataSet = new List<long>(); 


     for (int i = MIN_NUMBER; i <= MAX_NUMBER; ++i) 
     { 
      dataSet.Add(i); 
     } 

     for (int i = 0; i < NUMBER_OF_RESULTS_REQUIRED; ++i) 
     { 
      int t = randomObject.Next(0, dataSet.Count); 
      randomArr[i] = dataSet[t]; 
      dataSet.RemoveAt(t); 
     } 
     for (int i = 0; i < randomArr.Length; ++i) 
     { 
      Console.WriteLine(randomArr[i]); 
     } 
     Console.ReadKey(); 
    }