【问题标题】:Number of arrangements安排数量
【发布时间】:2008-12-09 23:41:15
【问题描述】:

假设我们有 n 个元素,a1a2, ...,an,排成一圈。即a2a1a3之间a3a2a4anan之间>-1a1 等等。

每个元素可以取值 1 或 0。如果有对应的 ai 的值,则两种排列是不同的不同。例如,当 n=3 时,(1, 0, 0) 和 (0, 1, 0) 是不同的排列,即使它们在旋转或反射下可能是同构的。

因为有n个元素,每个元素可以取两个值,所以排列的总数是2n

问题来了:

可能有多少种排列方式,使得没有两个相邻元素的值都为 1?如果有帮助,请仅考虑 n>3 的情况。

我在这里问有几个原因:

  1. 这是在我解决编程问题时出现的
  2. 听起来问题可能受益于布尔逻辑/位算术
  3. 也许没有封闭的解决方案。

【问题讨论】:

  • 澄清一下,圆是相邻的含义的定义——元素 n 与元素 1 相邻——但您不认为相同位模式的旋转是相同。对吗?

标签: algorithm math


【解决方案1】:

我们先问一个问题“有多少个长度为 n 的 0-1 序列没有两个连续的 1?”假设答案是 A(n)。我们有 A(0)=1(空序列),A(1) = 2(“0”和“1”),以及 A(2)=3(“00”,“01”和“10”,但是不是“11”)。

为了更容易编写递归,我们将 A(n) 计算为两个数字的总和:
B(n),以 0 结尾的此类序列的数量,以及
C(n),以 1 结尾的此类序列的数量。

然后 B(n) = A(n-1) (取任意长度为 n-1 的序列,并附加一个 0)
和 C(n) = B(n-1) (因为如果在 n 位置有 1,那么在 n-1 处必须有 0。)
这给出了 A(n) = B(n) + C(n) = A(n-1) + B(n-1) = A(n-1) + A(n-2)。 现在应该很熟悉了:-)

A(n) 就是斐波那契数 Fn+2,其中斐波那契数列定义为
F0=0, F1 =1,且 Fn+2= Fn+1+Fn,对于 n ≥ 0。

现在回答你的问题。我们将分别计算 a1=0 和 a1=1 的排列数量。对于前者,a2 … an 可以是任意序列(没有连续的 1),所以数为 A(n-1)=Fn+1。对于后者,我们必须有 a2=0,然后 a3…an 是任何没有连续 1 的序列 以0结尾,即B(n-2)=A(n-3)=Fn-1

所以答案是 Fn+1 + Fn-1

实际上,我们可以比那个答案更进一步。请注意,如果您将答案称为
G(n)=Fn+1+Fn-1,则
G(n+1)=Fn+2+Fn
G(n+2)=Fn+3+Fn+1,所以即使是 G(n) 也满足与斐波那契数列相同的递归! [实际上,类似斐波那契序列的任何线性组合都将满足相同的递归,所以这并不令人惊讶。] 所以另一种计算答案的方法是使用:
G(2)=3
G(3)=4
对于n≥4,G(n)=G(n-1)+G(n-2)。

现在您还可以使用closed form Fn=(αnn)/(α-β) (其中 α 和 β 为 (1±√5)/2,x2-x-1=0)的根,得到
G(n) = (( 1+√5)/2)n + ((1-√5)/2)n.
[您可以忽略第二项,因为它对于大 n 非常接近 0,实际上 G(n) 是 最接近 ((1+√5)/2)的整数n 对于所有 n≥2。]

【讨论】:

  • 手动检查 2、3、4。我认为关于构造 B(n) 和 C(n) 的论点通过从长度为 1 的两种情况中推断来强制结束的正确性。
  • 参见“对于后者,我们必须有 a2=0,然后 a3……an 是任何没有连续 1 的序列以 0 结尾”——这就是我认为序列不能同时以 1 开始和结束。
【解决方案2】:

我决定编写一个小脚本来尝试一下:

#!/usr/bin/python
import sys

# thx google 
bstr_pos = lambda n: n>0 and bstr_pos(n>>1)+str(n&1) or ""

def arrangements(n):
    count = 0
    for v in range(0, pow(2,n)-1):
        bin = bstr_pos(v).rjust(n, '0')
        if not ( bin.find("11")!=-1 or ( bin[0]=='1' and bin[-1]=='1' ) ):
            count += 1
            print bin
    print "Total = " + str(count)

arrangements(int(sys.argv[1]))

运行 5 次,总共给了我 11 种可能性,00000, 00001, 00010, 00100, 00101, 01000, 01001, 01010, 10000, 10010, 10100.

附: - 请原谅上面代码中的 not()。

【讨论】:

  • 很好地证实了“理论”(另一个答案),因为 F(6)=8 和 F(4)=3,所以 G(5)=F(6)+F(4)确实是 11 :-)
【解决方案3】:

将我幼稚的脚本加入其中。缓存部分结果的机会很多,但对于小的 n 运行速度足够快,我没有打扰。

def arcCombinations(n, lastDigitMustBeZero):
    """Takes the length of the remaining arc of the circle, and computes
       the number of legal combinations.
       The last digit may be restricted to 0 (because the first digit is a 1)"""

    if n == 1: 
        if lastDigitMustBeZero:
            return 1 # only legal answer is 0
        else:
            return 2 # could be 1 or 0.
    elif n == 2:
        if lastDigitMustBeZero:
            return 2 # could be 00 or 10
        else:
            return 3 # could be 10, 01 or 00
    else:
        # Could be a 1, in which case next item is a zero.
        return (
            arcCombinations(n-2, lastDigitMustBeZero) # If it starts 10
            + arcCombinations(n-1, lastDigitMustBeZero) # If it starts 0
            )

def circleCombinations(n):
    """Computes the number of legal combinations for a given circle size."""

    # Handle case where it starts with 0 or with 1.
    total = (
            arcCombinations(n-1,True) # Number of combinations where first digit is a 1.
            +
            arcCombinations(n-1,False) # Number of combinations where first digit is a 0.
        )
    return total


print circleCombinations(13)

【讨论】:

    【解决方案4】:

    这个问题和Zeckendorf representations很相似。由于循环约束,我找不到应用 Zeckendorf 定理的明显方法,但斐波那契数显然在这个问题中非常普遍。

    【讨论】:

      猜你喜欢
      • 2021-12-15
      • 1970-01-01
      • 1970-01-01
      • 2021-01-06
      • 2021-11-19
      • 1970-01-01
      • 1970-01-01
      • 2018-10-21
      • 1970-01-01
      相关资源
      最近更新 更多