2014-09-18 35 views
1

我有這樣的代碼,請從LINQ查詢LINQ選擇定製隨機岬

Random rand = new Random(); 
int TotalFound = Query.Count(); 
if (TotalFound > 0) { 
    int toSkip = rand.Next(0, TotalFound); 
    Video Result = Query.Skip(toSkip).Take(1).First(); 
    return Result; 
} else 
    return null; 

隨機進入現在,我想添加一個名爲「偏愛」查詢中的列,它是介於0的數和10. Preference值爲8的行將被選擇爲Preference值爲4的行的兩倍。更可能選擇9.8的值。

什麼是實現這種算法的有效方法?

作爲第二步,我可以添加一個參數,允許8值爲3x或4x行設置爲4,用指數曲線而不是線性曲線對結果進行微調。

我真的不知道如何去實現這個效率。

下面是我實現

public static int? SelectRandomId(IQueryable<Media> query, out int totalFound) { 
    int? Result = null; 

    // Pull list of ID and Preference from database. 
    var IdList = query.Select(v => new { ID = v.MediaId, Preference = v.Preference }).ToList(); 
    totalFound = IdList.Count(); 

    // Calculate preferences average and total. 
    int PreferenceCount = IdList.Where(v => v.Preference.HasValue).Count(); 
    int NoPreferenceCount = IdList.Count() - PreferenceCount; 
    int PreferenceSum = IdList.Where(v => v.Preference.HasValue).Sum(v => PreferenceToInt(v.Preference.Value)); 
    // Use value 10 for every item if it is not specified for any. 
    int PreferenceAvg = (PreferenceCount > 0 ? PreferenceSum/PreferenceCount : 10); 
    // Videos with no preference get the average value. 
    int PreferenceTotal = PreferenceSum + NoPreferenceCount * PreferenceAvg; 

    // Get a random number between zero and the sum of all the preferences 
    Random rand = new Random(); 
    int number = rand.Next(0, PreferenceTotal); 

    int rollingSumOfPreferences = 0; 

    // Set default value in case a value doesn't get assigned by the loop. 
    if (totalFound > 0) 
     Result = IdList[0].ID; 

    // Select an index from the video list, but weighted by preference 
    foreach (var item in IdList) { 
     // Add the current item's preference to the rolling sum 
     if (item.Preference.HasValue) 
      rollingSumOfPreferences += PreferenceToInt(item.Preference.Value); 
     else 
      rollingSumOfPreferences += PreferenceAvg; 

     // If we've hit or passed the random number, select this item 
     if (rollingSumOfPreferences >= number) { 
      Result = item.ID; 
      break; 
     } 
    } 

    return Result; 
} 

private static int PreferenceToInt(float preference) { 
    return (int)(Math.Pow(preference, 1.2) * 100); 
} 
+0

這是LINQ to SQL的,還是什麼? – 2014-09-18 23:51:34

+0

Linq to Entities – 2014-09-19 00:00:14

回答

1

我認爲它可能工作,如果你在0[the sum of all preferences]之間隨機選擇。然後,您可以遍歷所有項目並將項目首選項的滾動總和存儲在另一個變量中。一旦您擊中滾動總和等於或大於隨機數的項目,請選擇該項目。這應該更喜歡按降序排列的較大偏好。至少在我的測試中它起作用了!

希望這個代碼可以更好地解釋它:

private class Video 
{ 
    public string Name { get; set; } 
    public int Preference { get; set; } 
} 

public static void GenericTester() 
{ 
    // Initialize array with item name and preference 
    var videos = new List<Video> 
    { 
     new Video {Name = "ten", Preference = 10}, 
     new Video {Name = "nine", Preference = 9}, 
     new Video {Name = "eight", Preference = 8}, 
     new Video {Name = "seven", Preference = 7}, 
     new Video {Name = "six", Preference = 6}, 
     new Video {Name = "five", Preference = 5}, 
     new Video {Name = "four", Preference = 4}, 
     new Video {Name = "three", Preference = 3}, 
     new Video {Name = "two", Preference = 2}, 
     new Video {Name = "one", Preference = 1}, 
     new Video {Name = "zero", Preference = 0} 
    }; 

    // Dictionary to store results of how many times each 
    // preference was selected (for testing purposes) 
    var results = new Dictionary<int, int>(); 
    for (int i = 0; i <= videos.Max(v => v.Preference); i++) 
    { 
     results[i] = 0; // Init all items to zero 
    } 

    // Init random number generator 
    var rand = new Random(); 

    for (int n = 1; n < 100000; n++) 
    { 
     // Get a random number between zero and the sum of all the preferences 
     var number = rand.Next(0, videos.Sum(v => v.Preference)); 

     // Initialize index to the highest preference 
     var index = videos.Max(v2 => v2.Preference); 
     var rollingSumOfPreferences = 0; 

     // Select an index from the video list, but weighted by preference 
     foreach(var video in videos) 
     { 
      // Add the current item's preference to the rolling sum 
      rollingSumOfPreferences += video.Preference; 

      // If we've hit or passed the random number, select this item 
      if (rollingSumOfPreferences >= number) 
      { 
       index = video.Preference; 
       break; 
      } 
     } 

     // Increment the count for the selected preference 
     results[index]++; 
    } 

    foreach (var result in results) 
    { 
     Console.WriteLine("The preference value '{0}' was selected '{1}' times.", result.Key, result.Value); 
    } 
} 
+0

我會沿着這些路線嘗試一些東西。我想我會從數據庫中提取ID和首選項列表,然後進行分析以從內存中選擇一個ID。如果有100個元素可供選擇,那麼從數據庫中拉出200個數字就沒什麼大不了的。好於多次運行復雜查詢。 – 2014-09-20 21:57:33

0

隨機函數提供了一個比較均勻的分佈等它自己的它不會給你想要的東西的解決方案。下面埃裏克利珀本文提供了一些背景並在正確方向邁出的一步(因爲許多非均勻分佈是通過轉化的均勻分佈來實現):產生良好的統計分佈可以是相當複雜的

http://blogs.msdn.com/b/ericlippert/archive/2012/02/21/generating-random-non-uniform-data-in-c.aspx

代碼很多人使用第三方庫。這一個是相當不錯的:

http://www.extremeoptimization.com/

如果你想擁有你自己的分佈的裂縫,MSDN文檔顯示如何重寫的抽樣方法:

http://msdn.microsoft.com/en-us/library/system.random.sample(v=vs.110).aspx

假設你可以創作或找到滿足您需求的隨機分佈我會做類似以下的事情:

Random uniformDist = new Random(); 
YourRand yourDist = new YourRand(); 
int groupToSelect = yourDist.Next(1, 10); 
int TotalFound = Query.Where(v => v.Preference == groupToSelect).Count(); 
if (TotalFound > 0) { 
    int toSkip = uniformDist.Next(0, TotalFound); 
    Video Result = Query.Where(v => v.Preference == groupToSelect).Skip(toSkip).Take(1).First(); 
    return Result; 
} else 
    return null; 

D根據數據庫中的行數,我會運行SQL分析器來確保您可以獲得足夠的性能。