【问题标题】:Randomly break up array into chunks of at least 3 with even distribution随机将数组分成至少 3 个且分布均匀的块
【发布时间】:2014-12-23 20:30:19
【问题描述】:

我有一个大小为 n 的数组,并且想将其分成 m 个大小至少为 3 的块。例如, 给定数组

[1,2,3,4,5,6,7,8,9,10]

而 m=3,我们可以把它分解成

a=[1,2,3,4][5,6,7][8,9,10]
b=[1,2,3][4,5,6,7][8,9,10]
c=[1,2,3][4,5,6][7,8,9,10]

我们可以将这些解决方案视为由 (4,3,3) (3,4,3) 和 (3,3,4) 对表示。 我想要一个给定数组 n 和 m 的函数,它返回一个随机解决方案,并以均匀分布返回这些解决方案(这样您就不太可能得到一个特定的解决方案而不是其他任何解决方案)。 (此函数需要在 n=50 时工作,因此出于性能原因,我们不能通过计算所有可能的解来做到这一点。)

因此,在上述情况下,此方法将返回 [4,3,3] 三分之一的时间,[3,4,3] 三分之一的时间,以及 [3,3,4] 三分之一的时间时间。

【问题讨论】:

  • 你试过什么?您需要付出一些努力,然后我们可以帮助您修复任何您无法解决的错误。
  • 我不明白你得到了什么,你回报了什么
  • 上述情况下的返回值为 [4,3,3]、[3,4,3] 和 [3,3,4]。 (它总是返回一个总和为 n 的 m 个整数的列表)。
  • 要查看我目前正在运行什么,请查看github.com/jaredjstewart/MultipleDepotVehicleRoutingProblem/… 的 rand_breaks_array() 方法.在我看来应该有一个更简单的方法!
  • 所以,上面链接的方法有效,我只是怀疑存在一个我尚未发现的优雅解决方案。

标签: java arrays random groovy


【解决方案1】:

只是一个想法:

假设 m=3,n=20。我们能做的就是这样做:

  1. 在 m 和 n - 2*m(3 和 14 之间)之间选择一个数字
  2. 假设我们随机 6 个。这将是第一组,我们称之为 p1
  3. 在 [m, [n - m - p1]] 的新子集之间选择一个数字,即 [3, 20 - 6 - 3] 或 [3, 11] 的子集
  4. 假设我们掷出 10。这是 p2
  5. 余数(或 p3)的大小将为 20 -p1 -p2 = 4

最终集将是 [6, 10, 4]

这行得通吗?它也不需要对原始列表进行任何迭代。您唯一的迭代将超过 m 并且不依赖于 n。

我可以尝试使变量 m 更通用(第 1 步需要稍作更改,第 3 步和第 5 步将处于循环中)但我相信如果此解决方案可以接受,您可以解决给你。 步骤 1 的示例重写将是:

Choose a number between m and n - [m - 1] * m

【讨论】:

    【解决方案2】:

    这行得通吗?

    def randomCollate(item, chunk) {
        def collated = item.collate( chunk )
        def remainder = collated.reverse().takeWhile { it.size() != chunk }.flatten()
        def randomIdx = new Random().nextInt( ( collated - remainder ).size() )
        collated[randomIdx] += remainder
        collated - [ remainder ]
    }
    
    randomCollate( 1..50, 3 )
    

    【讨论】:

      【解决方案3】:

      我刚刚的另一个想法

      1. 计算您将拥有多少个大小为 m 的数组以及多少个大小为 m+1(或不同的 sice)的数组。我们将这些值称为 x 和 y。
      2. 计算可能排列的数量 [3,3,4],[3,4,3],[4,3,3] -> 3 个排列 (x+y)Py(二项式系数)
      3. 从 0 中选择一个随机排列 - 可能的排列。让我们称之为 Z。
      4. 从现在开始,我不完全知道如何做到这一点,但我会试试这个:
      5. 假设您有一个包含 n 位的二进制数,其中 y 位为零。获取恰好 y 个零和 x 个 1 的 Zst 个可能的二进制数。
      6. 二进制示例:100101101
      7. 您的结果将是 [x,y,y,x,y,x,x,y,x]

      希望你明白我的意思。

      【讨论】:

        【解决方案4】:

        对于每个步骤,允许n-m*min 的随机范围(min 在您的示例中为 3);然后从该范围内选择一个数字,添加min,作为r。如果m2,则返回rn-r 的列表。否则返回rn-r, m-1 递归调用的结果。随机播放,你就有了随机大小的块。

        rnd = new Random()
        
        // build the chunk size list to randomly split `n` elements in `m` parts, 
        // where each part is at least of size `min`
        // needs a shuffle afterwards
        def s(n,m,min=3) {
            def l = n-m*min // the range where we can pick a random offset
            def r = min + (l?rnd.nextInt(l):0) // result for this step with optional random part
            m==2 ? [r,n-r] : [r]+s(n-r,m-1) // only two remaining? pick result and remainder, or recurse
        }
        
        def tests = [[9,3,3],[10,3,3],[27,9,3],[4,2,2]]
        1000.times{
            tests.each{ n,m,min ->
                def r = s(n,m,min) 
                assert r.sum()==n
                assert r.size()==m
                assert r.every{ it>= min }
            }
        }
        
        def items = (0..9).collect() // test list
        def slices = s(items.size(),3) // get the chunk sizes
        Collections.shuffle(slices) // shuffle the result
        // create ranges and use them to get the slices; should be another one or two methods
        println slices.inject([sum:0,result:[]]) { r,s -> r.result<<items[((r.sum)..(r.sum+s-1))]; r.sum+=s; r }.result
        //=> e.g. [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]
        

        【讨论】:

          猜你喜欢
          • 2014-12-29
          • 1970-01-01
          • 1970-01-01
          • 2011-08-08
          • 1970-01-01
          • 2020-12-12
          • 2021-12-28
          • 2013-12-09
          • 1970-01-01
          相关资源
          最近更新 更多