2009-10-17 25 views
10

Tôi có một giá trị, giả sử 20010. Tôi muốn chia ngẫu nhiên giá trị này trong 24 giờ. Vì vậy, về cơ bản chia giá trị thành một mảng lớn 24 khe nơi tất cả các khe đều ngẫu nhiên lớn.Giá trị chia nhỏ trong 24 phần có kích thước ngẫu nhiên bằng C#

Điều gì có thể là cách tốt để giải quyết vấn đề này bằng C#?

+0

Bạn có thể giải thích thêm một chút không? – BobbyShaftoe

+2

Tôi nghĩ rằng ==> Lấy giá trị 20010 và chia ngẫu nhiên thành 24 giá trị riêng biệt, chẳng hạn như cộng chúng lại với nhau tạo thành giá trị ban đầu. – Gregory

Trả lời

23

Vẽ 23 (không 24) số một cách ngẫu nhiên, (không có bản sao), trong khoảng từ 1 tới 20009. Thêm 0 và 20010 danh sách và đặt mua những con số này, sự khác biệt giữa mỗi thứ hai liên tiếp mang đến cho bạn một giá trị vị trí.

Một cách tiếp cận trực tuyến cũng có thể bằng cách vẽ một giá trị tại một thời điểm và trừ nó khỏi "nồi", vẽ lại khi số lớn hơn số lượng còn lại. Tuy nhiên, cách tiếp cận này có thể dẫn đến độ lệch lớn hơn về kích thước của các khe.

1

Điều này sẽ cung cấp cho bạn một chút "giảm" ngẫu nhiên chỉ số càng cao. Bạn có thể ngẫu nhiên vị trí danh sách nếu cần? Nó phụ thuộc vào những gì bạn cần làm với nó.

int initialValue = 20010; 
var values = new List<int>(); 

Random rnd = new Random(); 
int currentRemainder = initialValue; 

for (int i = 0; i < 21; i++) 
{ 
    //get a new value; 
    int val = rnd.Next(1, currentRemainder - (21 - i)); 

    currentRemainder -= val; 
    values.Add(val); 
} 

values.Add(currentRemainder); 

//initialValue == values.Sum() 
+1

Bạn có thể xem xét đổi tên currentRemainder thành một cái gì đó như restPortion. Sử dụng phần còn lại có thể hơi khó hiểu một chút, bởi vì không có sự phân chia nào liên quan. Chỉ là một ý nghĩ. –

+0

Điểm tốt, được thực hiện. – Gregory

+0

Có câu hỏi về phân phối. Có thể chấp nhận khi số ngẫu nhiên đầu tiên là 20010 - 21, rằng mọi số "ngẫu nhiên" sau đó là "1"? –

11

Giả sử bạn không muốn có nhiều (bất kỳ) kiểm soát việc phân phối kích thước, đây là một cách tiếp cận có thể hoạt động (mã giả).

  1. Tạo một danh sách 24 giá trị ngẫu nhiên, tạo ra tuy nhiên bạn thích, ở bất cứ quy mô
  2. Tìm tổng của danh sách này
  3. Tạo danh sách cuối cùng của bạn bằng cách điều chỉnh tỷ lệ 24 giá trị ngẫu nhiên chống lại tổng số của bạn

Ghi chú

  • Nếu bạn sử dụng dấu chấm động số học, bạn có thể bị mất một hoặc hai lần. Để tránh điều này, không sử dụng mở rộng quy mô để hoàn thành giá trị cuối cùng, thay vào đó hãy điền nó với tổng số còn lại.
  • Nếu bạn do cần kiểm soát chặt chẽ hơn việc phân phối, hãy sử dụng một phương pháp khác để tạo mảng ban đầu của bạn, nhưng phần còn lại không cần thay đổi.
+0

+1 giải pháp tốt mate (tôi đã không chắc chắn đây là câu hỏi thực sự) – Letterman

+0

Tôi đang cố gắng để hiểu đề nghị này, nhưng tôi không hiểu những gì "bằng cách mở rộng 24 giá trị ngẫu nhiên so với tổng số" phương tiện của bạn. Bất cứ ai có thể cung cấp một ví dụ những gì chính xác có nghĩa là trong mã? – Max

+2

double maxValue = 100; var values ​​= new List {1000, 2000, 3000, 4000, 5000}; double scalingFactor = maxValue/values.Sum(); var scaledValues ​​= values.Select (itm => itm * scalingFactor); var scaledValuesSum = scaledValues.Sum(); – Gregory

3

Nếu bạn muốn chắc chắn rằng bạn không thiên vị quá trình mà không cần phân tích nhiều, bạn chỉ cần tạo mảng 24 phần tử, khởi tạo mỗi phần tử thành 0 và sau đó thêm 1 vào một trong các thành phần ngẫu nhiên 20010 lần.

Tất cả phụ thuộc vào loại bản phân phối bạn muốn xem, nhưng tôi không nghĩ bất kỳ kỹ thuật nào khác được đề xuất cho đến nay sẽ dẫn đến "xô" kéo dài một giờ thống kê không thể phân biệt được.

23

Dưới đây là một giải pháp chức năng sử dụng thuật toán MJV của:

static int[] GetSlots(int slots, int max) 
{ 
    return new Random().Values(1, max) 
         .Take(slots - 1) 
         .Append(0, max) 
         .OrderBy(i => i) 
         .Pairwise((x, y) => y - x) 
         .ToArray(); 
} 

public static IEnumerable<int> Values(this Random random, int minValue, int maxValue) 
{ 
    while (true) 
     yield return random.Next(minValue, maxValue); 
} 

public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TSource, TResult> resultSelector) 
{ 
    TSource previous = default(TSource); 

    using (var it = source.GetEnumerator()) 
    { 
     if (it.MoveNext()) 
      previous = it.Current; 

     while (it.MoveNext()) 
      yield return resultSelector(previous, previous = it.Current); 
    } 
} 

public static IEnumerable<T> Append<T>(this IEnumerable<T> source, params T[] args) 
{ 
    return source.Concat(args); 
} 
+3

Mã này có vẻ quá thông minh một nửa. Nó có nghệ thuật không? – PeterAllenWebb

+0

Trong con mắt của kẻ đối xử, tôi cho là vậy. Tôi thích rằng việc thực hiện cốt lõi đọc gần như chính xác như mjv mô tả nó. – dahlbyk

+0

thực sự - tôi nghĩ rằng đây là một giải pháp thẳng về phía trước thanh lịch .. thông minh, nhưng không phải là 'quá' thông minh. – headsling

2

Một lựa chọn khác sẽ được tạo ra một số ngẫu nhiên giữa 0 và số mục tiêu. Sau đó, thêm từng phần "" vào danh sách. Chọn "mảnh" lớn nhất và cắt nó thành hai, sử dụng một số ngẫu nhiên khác. Chọn lớn nhất từ ​​danh sách (bây giờ với ba phần), và tiếp tục cho đến khi bạn có số lượng mong muốn của miếng.

List<int> list = new List<int>(); 
list.Add(2010); 
Random random = new Random(); 
while (list.Count() < 24) 
{ 
    var largest = list.Max(); 
    var newPiece = random.Next(largest - 1); 
    list.Remove(largest); 
    list.Add(newPiece); 
    list.Add(largest - newPiece); 
} 
1
class Numeric 
    def n_rands(n) 
    raw = (1..n).map { |x| rand } 
    raw.map { |x| x * to_f/raw.sum.to_f }.map { |x| x.to_i }.tap do |scaled| 
     scaled[-1] = self - scaled[0..-2].sum 
    end 
    end 
end 

puts 1000.n_rands(10).inspect # [22, 70, 180, 192, 4, 121, 102, 179, 118, 12] 
4

Tôi đã tính toán kích thước trung bình của mỗi người trong số 24 thùng hơn 100 thử nghiệm cho mỗi người trong số các thuật toán đề xuất ở đây. Tôi nghĩ rằng điều thú vị là ba trong số bốn dường như kết quả là trung bình 20010/24 mục mỗi thùng, nhưng phương pháp ngây thơ mà tôi mô tả hội tụ với mức trung bình đó một cách nhanh chóng nhất. Điều này làm cho một số cảm giác trực quan với tôi. Phương pháp đó giống như tuyết rơi ngẫu nhiên trên 24 thùng, và do đó có khả năng dẫn đến các thùng có kích thước xấp xỉ bằng nhau. Những người khác giống như hacking ngẫu nhiên ở độ dài của gỗ.

Bevan: [751, 845, 809, 750, 887, 886, 838, 868, 837, 902, 841, 812, 818, 774, 815, 857, 752, 815, 896, 872, 833, 864, 769, 894] 
Gregory: [9633, 5096, 2623, 1341, 766, 243, 159, 65, 21, 19, 16, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2] 
mjv: [895, 632, 884, 837, 799, 722, 853, 749, 915, 756, 814, 863, 842, 642, 820, 805, 659, 862, 742, 812, 768, 816, 721, 940] 
peterallenwebb: [832, 833, 835, 829, 833, 832, 837, 835, 833, 827, 833, 832, 834, 833, 836, 833, 838, 834, 834, 833, 834, 832, 836, 830] 

Và đây là đoạn code python: nhập khẩu ngẫu nhiên

N = 20010; 

def mjv(): 
    gaps = [ random.randrange(0, N) for i in range(0, 24) ] 
    gaps = gaps + [0, N] 
    gaps.sort() 
    value = [ gaps[i+1] - gaps[i] for i in range(0, 24) ] 
    return value 

def gregory(): 
    values = [] 
    remainingPortion = N 

    for i in range(0, 23): 
     val = random.randrange(1, remainingPortion - (23 - i)) 
     remainingPortion = remainingPortion - val 
     values.append(val) 

    values.append(remainingPortion) 

    return values 


def peterallenwebb(): 
    values = [0 for i in range(0, 24) ] 

    for i in range(0, N): 
     k = random.randrange(0, 24) 
     values[k] = values[k] + 1 

    return values 

def bevan(): 
    values = []; 
    sum = 0.0 

    for i in range(0, 24): 
     k = random.random() 
     sum = sum + k 
     values.append(k); 

    scaleFactor = N/sum 

    for j in range(0, 24): 
     values[j] = int(values[j] * scaleFactor) 

    return values 


def averageBucketSizes(method): 
    totals = [0 for i in range(0, 24)] 
    trials = 100 

    for i in range(0,trials): 
     values = method() 

     for j in range(0, 24): 
      totals[j] = totals[j] + values[j]  

    for j in range(0, 24): 
     totals[j] = totals[j]/trials 

    return totals; 


print 'Bevan: ', averageBucketSizes(bevan) 
print 'Gregory: ', averageBucketSizes(gregory) 
print 'mjv: ', averageBucketSizes(mjv) 
print 'peterallenwebb: ', averageBucketSizes(peterallenwebb) 

Hãy cho tôi biết nếu bạn thấy bất kỳ sai lầm. Tôi sẽ chạy lại.

+0

Điều này hầu như không thể đọc được. Mã này nhắc tôi những chương trình tôi viết ở trường trung học. – zvolkov

+0

Xin lỗi về việc thiếu khoảng trắng ngang. Đây chỉ là chương trình python thứ hai của tôi. Tôi đã thêm không gian trắng bây giờ mà tôi nhận ra rằng con trăn không quan tâm. Tuy nhiên tôi không nghĩ rằng nó làm cho một sự khác biệt lớn. Làm thế nào bạn sẽ viết alg của mjv trong python, ví dụ? – PeterAllenWebb

+0

@zkolkov có thể có nghĩa là nó không đơn điệu. Ví dụ, bevan là nhiều pythonic như: 'def bevan (N, sample_size): giá trị = [random.random() cho _ trong phạm vi (sample_size)] scaleFactor = N/tổng (giá trị) return [int (value * scaleFactor) cho giá trị trong các giá trị] 'mjv:' def mjv (N, sample_size): khoảng trống = [0] + được sắp xếp (random.randrange (0, N) cho _ trong phạm vi (sample_size-1)) + [ N] trả về [khoảng trống [i + 1] - khoảng trống [i] cho i trong phạm vi (sample_size)] 'hoặc' def mjv (N, kích thước): khoảng trống = được sắp xếp (random.randrange (0, N) cho _ trong phạm vi (size-1)) trả về [y - x cho x, y trong zip ([0] + khoảng trống, khoảng trống + [N])] ' – hughdbrown

2

Đây là một giải pháp khác mà tôi nghĩ sẽ hoạt động rất tốt cho việc này. Mỗi lần phương thức được gọi, nó sẽ trả về một tập hợp các giá trị được phân phối ngẫu nhiên khác.

public static IEnumerable<int> Split(int n, int m) 
{ 
    Random r = new Random(); 
    int i = 0; 

    var dict = Enumerable.Range(1, m - 1) 
     .Select(x => new { Key = r.NextDouble(), Value = x }) 
     .OrderBy(x => x.Key) 
     .Take(n - 2) 
     .Select(x => x.Value) 
     .Union(new[] { 0, m }) 
     .OrderBy(x => x) 
     .ToDictionary(x => i++); 

    return dict.Skip(1).Select(x => x.Value - dict[x.Key - 1]); 
} 
+0

Sử dụng thông minh từ điển để bắt chước Pairwise! Tôi có thể gắn bó với r.Next (1, m-1) để phân ngẫu nhiên các khoảng - xáo trộn trình tự của tất cả 20010 mục bổ sung thêm khá nhiều chi phí. Ngoài ra, Concat thích hợp hơn với Union nếu bạn không cần thao tác thiết lập thực tế - Union có thêm logic để loại bỏ các bản sao, điều này không thể xảy ra ở đây. – dahlbyk

6

Điều này thật thú vị. Lấy cảm hứng từ David, đây là một triển khai giải pháp của mjv chỉ sử dụng các toán tử do LINQ cung cấp. Vì khóa Từ điển của David chỉ là một chỉ mục, chúng tôi có thể sử dụng một mảng thay cho chức năng Pairwise:

var r = new Random(); 
var a = Enumerable.Repeat(null, n - 1)  // Seq with (n-1) elements... 
        .Select(x => r.Next(1, m)) // ...mapped to random values 
        .Concat(new [] { 0, m }) 
        .OrderBy(x => x) 
        .ToArray(); 

return a.Skip(1).Select((x,i) => x - a[i]); 
+0

Ooh! Thậm chí còn tốt hơn! Tôi thích nó! Bây giờ chúng ta đang tìm đến một số mã _real concise_. –

1

Tôi đã thử các giải pháp từ David và dahlbyk nhưng không có may mắn. Vì vậy, đây là những gì tôi đưa ra sau khi đọc câu trả lời từ mjv:

public static class IntExtensions 
{ 
    public static IEnumerable<int> Split(this int number, int parts) 
    { 
     var slots = Enumerable.Repeat(0, parts).ToList(); 
     var random = new Random(); 

     while (number > 0) 
     { 
      var slot = random.Next(0, parts); 
      slots[slot]++; 
      number--; 
     } 
     return slots; 
    } 
} 
Các vấn đề liên quan