2014-09-26 117 views
0

如何將一個數字隨機分成幾個數字?如何在紅寶石中隨機分配一個數字?

例如:我有一個數字30,我想它隨機分成幾個數字,每個數字的大小爲3-10之間,且每個數字的大小是互不相同

結果可能是這樣的:[5,7,9,6,3],[9,10,3,8],...等

我試過了,但是我解決不了,請給我幫助。

+2

「隨機分成幾個數字」是模糊的。你必須準確解釋你的意思是「隨機」,但我希望你會有一些困難。 – 2014-09-26 01:17:03

+0

@Cary Swoveland,隨機,它意味着號碼的數字是隨機的 – user3673267 2014-09-26 01:31:22

+1

我認爲歧義是什麼意思是「分裂一個數字」。你是否要求一個隨機的數字加起來的原始數字的唯一加號? – 2014-09-26 01:31:54

回答

1

非常不錯的拼圖。我會去:

class Fixnum 
    def random_split(set = nil, repeats = false) 
    set ||= 1..self 
    set = [*set] 
    return if set.empty? || set.min > self || set.inject(0, :+) < self 
    tried_numbers = [] 
    while (not_tried = (set - tried_numbers).select {|n| n <= self }).any? 
     tried_numbers << number = not_tried.sample 
     return [number] if number == self 
     new_set = set.dup 
     new_set.delete_at(new_set.index(number)) unless repeats 
     randomized_rest = (self-number).random_split(new_set, repeats) 
     return [number] + randomized_rest if randomized_rest 
    end 
    end 
end 

30.random_split(3..10) 

一般來說,上面的代碼涵蓋了很多情況。你可以在沒有任何參數的情況下執行它,它會假定它是從1到給定數字中選擇數字,並且結果集不應該包含任何重複。您可以選擇傳遞給定數量的設置。如果您通過[1,2,3,4,4,4],則需要注意4不會重複3次以上。如果第二個參數設置爲true,它將允許設置元素在結果中出現兩次或更多次。

+1

你試過了嗎?100.random_split(1..10)' – bjhaid 2014-09-26 01:44:41

+0

@bjhaid - 謝謝,最終會返回零,但應該比檢查所有可能的情況更聰明。現在修復。 – BroiSatse 2014-09-26 01:47:37

+0

我認爲這個問題是不明確的,這使得它很難得到一個*正確*答案 – bjhaid 2014-09-26 01:48:47

0

不是在世界上最好的算法中(它的一些試驗和錯誤),但嘿,CPU週期很便宜如今...這應該對每一個號碼的工作:

def split_this_number_into_several_numbers_randomly(a_number, min_number_to_start_from) 
    random_numbers = [0] 

    until (random_numbers.inject(&:+) == a_number) 
    random_numbers << rand(min_number_to_start_from..a_number/3) # replace 30 here, I assumed you wanted up to 1/3 of the original number 
    if (r = random_numbers.detect { |x| random_numbers.count(x) > 1}) then random_numbers.delete(r) end # so we have all unique numbers 
    random_numbers.pop if random_numbers.inject(&:+) >= a_number - min_number_to_start_from && random_numbers.inject(&:+) != a_number 
    end 
    random_numbers.delete_if{ |x| x == 0 } 
end 

,當然,有些代碼對其進行測試:

all_true = true 
1000.times do 
    arr = split_this_number_into_several_numbers_randomly(30, 3) 
    all_true == false unless arr.inject(&:+) == 30 
    all_true == false unless arr.size == arr.uniq.size 
end 

p all_true #=> true 
0

的OP已確認關於該問題的是隨機選擇是,以反映下面的過程的註釋:「選擇3和10之間不同的數目的所有組合([3],[3, 4],[3,5,6,8,9],.. [9,10],[10]),拋出所有不加總和爲30的組合,並選擇o那些隨機離開的人「。以下是實現這一點的直接方式。可以提高效率,但這將是很多工作。

代碼

def arrays(sum, range) 
    largest_sum = (range.first+range.last)*(range.last-range.first+1)/2 
    (raise ArgumentError, 
    "largest sum for range = #{largest_sum}") if sum > largest_sum 
    avail = [*range] 
    b = -(2*range.last + 1.0) 
    c = 8.0*sum 
    min_nbr = ((-b - (b*b - c)**0.5)/2).ceil.to_i 
    max_nbr = ((-1.0 + (1.0 + c)**0.5)/2).to_i 
    (min_nbr..max_nbr).each_with_object([]) { |n, a| 
    a.concat(avail.combination(n).select { |c| c.inject(:+) == sum }) } 
end 

min_nbr注和max_nbr採用二次公式,以確定其可以總和爲sum不同號碼的數目的範圍內。

例子

sum = 30 
range = (3..10) 
arr = arrays(sum, range) # all combinations that sum to 30 
    #=> [[3, 8, 9, 10], [4, 7, 9, 10], [5, 6, 9, 10], [5, 7, 8, 10], 
    # [6, 7, 8, 9], 
    # [3, 4, 5, 8, 10], [3, 4, 6, 7, 10], [3, 4, 6, 8, 9], 
    # [3, 5, 6, 7, 9], [4, 5, 6, 7, 8]] 

(Solution time: well under 1 sec.) 

10.times { p arr[rand(arr.size)] } # 10 random selections 
    #=> [3, 4, 6, 8, 9] 
    # [3, 4, 6, 8, 9] 
    # [5, 7, 8, 10] 
    # [4, 5, 6, 7, 8] 
    # [3, 4, 5, 8, 10] 
    # [6, 7, 8, 9] 
    # [3, 4, 5, 8, 10] 
    # [4, 5, 6, 7, 8] 
    # [6, 7, 8, 9] 
    # [3, 4, 6, 7, 10] 

sum = 60 
range = (3..10) 
arr = arrays(sum, range) 
    #=> in `arrays': largest sum for range = 52 (ArgumentError) 

兩個更多...

sum = 60 
range = (3..20) 
arr = arrays(sum, range) # all combinations that sum to 60 
arr.size 
    #=> 1092 
(Solution time: about 1 sec.) 
10.times { p arr[rand(arr_size)] } # 10 random selections 
    #=> [12, 14, 15, 19] 
    # [3, 4, 6, 7, 11, 13, 16] 
    # [3, 6, 7, 9, 15, 20] 
    # [3, 8, 14, 17, 18] 
    # [3, 4, 5, 7, 10, 13, 18] 
    # [3, 5, 6, 7, 11, 13, 15] 
    # [5, 6, 7, 8, 14, 20] 
    # [4, 5, 9, 11, 15, 16] 
    # [4, 5, 8, 13, 14, 16] 
    # [3, 4, 5, 12, 16, 20] 

sum = 100 
range = (3..30) 
arr = arrays(sum, range) # all combinations that sum to 100 
arr.size 
    #=> 54380 
(Solution time: 3 or 4 minutes) 
10.times { p arr[rand(arr_size)] } # 10 random selections 
    #=> [3, 4, 6, 9, 11, 12, 15, 17, 23] 
    # [4, 5, 6, 7, 9, 13, 14, 17, 25] 
    # [4, 5, 6, 7, 11, 17, 21, 29] 
    # [9, 10, 12, 13, 17, 19, 20] 
    # [6, 9, 10, 23, 25, 27] 
    # [3, 4, 5, 6, 7, 8, 9, 14, 15, 29] 
    # [3, 4, 5, 6, 7, 8, 9, 15, 17, 26] 
    # [3, 4, 5, 6, 7, 8, 17, 22, 28] 
    # [3, 5, 6, 7, 9, 12, 13, 15, 30] 
    # [6, 8, 9, 10, 13, 15, 18, 21] 
1

分割數稱爲integer partition。下面是基於Marc-André Lafortune's recursive algorithm一個解決方案:

def expand(n, max = n) 
    return [[]] if n == 0 
    [max, n].min.downto(1).flat_map do |i| 
    expand(n-i, i).map{|rest| [i, *rest]} 
    end 
end 

expand(30).select { |a| a.size >= 3 && a.size <= 10 }.sample(5) 
#=> [[15, 3, 3, 3, 2, 2, 1, 1], 
# [9, 5, 4, 3, 2, 2, 2, 1, 1, 1], 
# [13, 10, 4, 2, 1], 
# [8, 8, 7, 2, 2, 1, 1, 1], 
# [8, 6, 4, 3, 3, 2, 1, 1, 1, 1]] 

注意可能的分區的數量變得非常大:30 5604個分區,100具有190569292個分區和1000具有2.4 × 1031分區。

+0

其他答案的作者 - 包括我 - 解釋問題爲隨機選擇''3'之間30'和'10'那筆一組不同的號碼之一。 (你可以用替換'a.size> = 3..''a.min> = 3 && a.max <= 10&a.reduce(:+)== 30',但是這不是非常有效的,因爲可能的設置大小的範圍可以預先確定,減少用'expand'生成的組合的數量。)也許OP會澄清。 – 2014-09-27 21:24:16