【问题标题】:Rank and unrank combinations to distribute k balls into n bins of different capacitiesrank 和 unrank 组合将 k 个球分配到 n 个不同容量的 bin 中
【发布时间】:2020-06-01 18:04:36
【问题描述】:

我想将k 球分配到不同容量的n 箱中。如何对给定 nk 和 bin 容量的分布进行排名和取消排名?

例子:

n := 3

k := 4

bin capacities := 3,2,1

垃圾箱中的球:

1,2,1, 2,1,1, 2,2,0, 3,0,1, 3,1,0 := 5

有公式吗?

【问题讨论】:

  • 排名和非排名是什么意思?看起来您想计算可以将 k 个球分配到 n 个箱中的方式的数量,每个箱中每个箱都装有不同的有限数量的球?
  • @doggie_breath 当人们说“rank and unrank”时,他们通常的意思是对球的分布进行排序,然后能够查看分布并说出它是哪个分布,然后查看一个数字m 并说出这是mth 分布。
  • 似乎缺少分布2,1,1。我假设列表是按字典顺序排列的,这应该是列表中的第二个?
  • @btilly 啊,明白了。感谢您的澄清。
  • 是的,2,1,1 不见了。

标签: arrays algorithm math combinations combinatorics


【解决方案1】:

我不知道这种技术是否有一个标准名称,但这是一种我已经成功解决了很多次的问题。

我使用动态编程来构建一个可以发生排名/取消排名的数据结构,然后构建逻辑来执行排名/取消排名的事情。

动态编程部分是最难的。

import collections
BallSolutions = collections.namedtuple('BallSolutions', 'bin count balls next_bin_solutions next_balls_solutions');

def find_ball_solutions (balls, bin_capacities):
    # How many balls can fit in the remaining bins?
    capacity_sum = [0 for _ in bin_capacities]
    capacity_sum[-1] = bin_capacities[-1]

    for i in range(len(bin_capacities) - 2, -1, -1):
        capacity_sum[i] = capacity_sum[i+1] + bin_capacities[i]

    cache = {}
    def _search (bin_index, remaining_balls):
        if len(bin_capacities) <= bin_index:
            return None
        elif capacity_sum[bin_index] < remaining_balls:
            return None
        elif (bin_index, remaining_balls) not in cache:
            if bin_index + 1 == len(bin_capacities):
                cache[(bin_index, remaining_balls)] = BallSolutions(
                    bin=bin_index, count=1, balls=remaining_balls, next_bin_solutions=None, next_balls_solutions=None)
            else:
                this_solution = None
                for this_balls in range(min([remaining_balls, bin_capacities[bin_index]]), -1, -1):
                    next_bin_solutions = _search(bin_index+1, remaining_balls - this_balls)
                    if next_bin_solutions is None:
                        break # We already found the fewest balls that can go in this bin.
                    else:
                        this_count = next_bin_solutions.count
                        if this_solution is not None:
                            this_count = this_count + this_solution.count
                        next_solution = BallSolutions(
                            bin=bin_index, count=this_count,
                            balls=this_balls, next_bin_solutions=next_bin_solutions,
                            next_balls_solutions=this_solution)
                        this_solution = next_solution
                cache[(bin_index, remaining_balls)] = this_solution
        return cache[(bin_index, remaining_balls)]

    return _search(0, balls)

以下是生成排名解决方案的代码:

def find_ranked_solution (solutions, n):
    if solutions is None:
        return None
    elif n < 0:
        return None
    elif solutions.next_bin_solutions is None:
        if n == 0:
            return [solutions.balls]
        else:
            return None
    elif n < solutions.next_bin_solutions.count:
        return [solutions.balls] + find_ranked_solution(solutions.next_bin_solutions, n)
    else:
        return find_ranked_solution(solutions.next_balls_solutions, n - solutions.next_bin_solutions.count)

这是生成解决方案排名的代码。请注意,如果提供的答案无效,它会爆炸。

def find_solution_rank (solutions, solution):
    n = 0
    while solutions.balls < solution[0]:
        n = n + solutions.next_bin_solutions.count
        solutions = solutions.next_balls_solutions
    if 1 < len(solution):
        n = n + find_solution_rank(solutions.next_bin_solutions, solution[1:])
    return n

这是一些测试代码:

s = find_ball_solutions(4, [3, 2, 1])
for i in range(6):
    r = find_ranked_solution(s, i)
    print((i, r, find_solution_rank(s, r)))

【讨论】:

    【解决方案2】:

    您可以递归地定义此类组合的数量。给定k 球和箱容量q_1, ..., q_n,对于0q_1 之间的每个j,将j 球放入q_1 并将剩余的k-j 球分配到其他箱中。

    这是一个快速的 Python 实现:

    from functools import lru_cache
    
    @lru_cache(None)
    def f(n, *qs):
      if not qs:
        return 1 if n == 0 else 0
      q = qs[0]
      return sum(f(n-j, *qs[1:]) for j in range(q+1))
    
    f(4, 3, 2, 1)
    # 5
    

    【讨论】:

      【解决方案3】:

      这是一种方法(在伪代码中),虽然它看起来效率不高。在球的数量不适合总剩余容量的地方添加一些短路可能是明智的。如果给定的容量列表将被多次使用,也许一些巧妙的缓存会有所帮助。

      所有数字都是非负整数。函数ArrayTail(array a) 是子数组,其元素是第一个之后的输入数组的所有元素。函数ArrayCon(number head, array a)是元素为head后跟a元素的数组。

      function Count(array capacities, number balls) -> number
          If balls == 0:
             return 1
          Else if capacities is empty:
             return 0
          Else:
             Let sum: number
             sum <- 0
             For b from 0 to max(balls, capacities[0]):
                 sum <- sum + Count(ArrayTail(capacities), b)
             End For
             return sum
          End If/Else
      End function
      
      function Rank(array capacities, array counts) -> number
          Precondition: length(capacities) == length(counts)
          Precondition: counts[i] <= capacities[i] for all i < length(counts)
          If counts is empty:
              return 0
          Else:
              Let total: number
              total <- 0
              For c in counts:
                  total <- total + c
              End For
              Let r: number
              r <- Rank(ArrayTail(capacities), ArrayTail(counts))
              For b from 0 to (counts[0]-1):
                  r <- r + Count(ArrayTail(capacities), total - b)
              End For
              return r
          End If/Else
      End function
      
      function Unrank(array capacities, number balls, number rank) -> array
          Precondition: rank < Count(capacities, balls)
          If capacities is empty:
              return empty array
          Else
              Let c0: number
              c0 <- 0
              Loop until "return":
                  Let subcount: number
                  subcount <- Count(ArrayTail(capacities), balls-c0)
                  If subcount <= rank:
                      c0 <- c0 + 1
                      rank <- rank - subcount
                  Else
                      return ArrayCon(c0, Unrank(ArrayTail(capacities), balls-c0, rank))
                  End If/Else
              End Loop
          End If/Else
      End function
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多