首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >满足给定约束的有效序列数

满足给定约束的有效序列数
EN

Stack Overflow用户
提问于 2022-04-07 04:28:09
回答 4查看 247关注 0票数 4

问题是找到具有给定约束的有效序列(A1,A2....AN)的数目:

  1. Ai值介于1到6之间(1<= Ai <= 6)
  2. 两个相邻的数字应该是相互关联的。例如,2,4,.不允许序列
  3. 如果值在序列中重复,那么它们的索引差应该大于2。例如,如果K=4,(1,2,3,1)是有效序列,而(1,3,4,3)不是有效序列 注:n在问题陈述中给出。

我只能想到一个回溯解决方案,其中每个递归调用都会对给定的Ai位置尝试从1到6的所有数字。

这看起来更像是蛮力的方式。你能帮我找到一个最佳的解决方案吗。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2022-04-07 07:45:52

我们可以利用这样一个事实,即只有6个可能的数字可以用来构造数字。

  1. 考虑一个查找表dp[i][j][k],它基本上是count of i-digit numbers with the i-th digit as j, (i-1)th digit as k
  2. 为每个数字创建一个所有有效的同素映射。有点像{2: [1,3,5], 3: [1,2,4,5], 6: [1,5]}...
  3. 基本案例:dp[0] = 0 for all j,kdp[1] = 0 for all j,kdp[2][j][k] = 1 if gcd(j,k)==1 and j!=k
  4. 现在应该把桌子装得比较直。
代码语言:javascript
复制
for i in range 2 to n:
for j in range 1 to 6:
for k in range 1 to 6
if j and k are not coprime, dp[i][j][k] = 0 
else, dp[i][j][k] = summation(dp[i-1][k][l]) where l is in range [1,6] AND l!=j and j!=k
# this ensures a min gap of 3 and maintains co-primality of adjacent numbers
  1. 最终答案= sum(dp[N][1..6])

这具有O(N*6*6) ~ O(N)的时间和空间复杂性。

编辑:@KellyBundy很好地添加了Python实现;纠正了它(以及我的回答中的一个小缺陷),并在这里添加:

代码语言:javascript
复制
def count_seq(N):
  A = range(1, 7)
  dp = [[[None] * 7 for _ in range(7)] for _ in range(N+1)]
  for j in A:
    for k in A:
      dp[0][j][k] = 0
      dp[1][j][k] = 0
      dp[2][j][k] = int(gcd(j,k)==1 and j!=k)
  for i in range(3, N+1):
    for j in A:
      for k in A:
        if gcd(j, k) != 1:
          dp[i][j][k] = 0 
        else:
          dp[i][j][k] = sum(dp[i-1][k][l] for l in A if (l!=j and j!=k))
  return sum(dp[N][j][k] for j in A for k in A)

N = 100
print(count_seq(N))
票数 3
EN

Stack Overflow用户

发布于 2022-04-07 07:32:25

这里有一个图搜索问题,它可以通过回溯搜索来解决,并且可以使用回忆录使其运行得更快。

定义州

按照问题中的规则,状态是元组,其中元组中的第一个值是当前数a_n,而序列a_{n-1}中的前一个数是序列的长度,因为数字的出现可以在2或更长的区间内。

此外,禁止状态是两个数不是共素数的状态,这意味着

代码语言:javascript
复制
number_set = [1, 2, 3, 4, 5, 6]

长度为2的是一个有效状态,但禁用集除外,即:

代码语言:javascript
复制
forbidden_tuples = {(2,4), (2,6), (4,6), (3,6), (4,2), (6,2), (6,4), (6,3)}

示例:((2,3), 5)处于状态( 2,3 ),所需的序列长度为5。在这种情况下,下列数字不能是2,3(保持至少2的距离)或6(保持相邻数的余素数),因此后续状态的总数为:

  1. (3,1),4)
  2. ((3, 4),4)
  3. ((3,5),4)

建立解决方案

我将提供的解决方案是一个递归的DFE图和回忆录的时间优化。解决办法如下:

代码语言:javascript
复制
import itertools

def count_seq(N, start_state=None, memoization=None):
    """
    :param N:
    :param start_state
    :param memoization
    :return: Rules:
    1. a_i in {1,2,3,4,5,6}
    2. Two adjacent numbers should be coprime. For e.g. 2,4,.. sequence is not allowed ( for the set {1,2,3,4,5,6} this means that (2,6),(2,4),(3,6),(4,6) are forbidden
    3. repetitions with a distance >= 2

    State are ordered as a 2 item tuple to remember the history: (a_{n-1} , a_n)
    """
    if N == 1:
        return 6
    if memoization is None:
        memoization = {}
    count = 0
    state_set = set()  # Pending states which have not been explored yet
    number_set = [1, 2, 3, 4, 5, 6]
    forbidden_tuples = {(2,4), (2,6), (4,6), (3,6), (4,2), (6,2), (6,4), (6,3)}
    if start_state is None: # Getting initial states
        for subset in itertools.permutations(number_set, 2):
            if subset in forbidden_tuples:
                pass
            else:
                state_set.add((subset, N))
    else:  # checking all possible next states and appending to queue with 1 less length
        for ii in number_set:
            if ii in start_state or (ii, start_state[-1]) in forbidden_tuples:
                pass
            else:
                state_set.add(((start_state[1:] + tuple([ii])), N-1))
    # for each possible next state
    for state in state_set:
        if state[1] <= 2:
            count += 1
        elif state in memoization:  # checking if we computed this already
            count += memoization[state]
        else:  #we did not compute this, doing the computation
            memoization[state] = count_seq(state[1], state[0], memoization)
            count +=  memoization[state]

    return count

探索

如果想要的长度为1,请返回6,因为这两个数字中的任何一个都可以位于第一个位置。

  1. 我们看到如果开始状态是None,我们假设这是初始调用,所以我们创建了长度为2的数字的所有可能的无禁止排列。否则,我们创建一组所有可能的下一个状态。
  2. 对于每一个可能的下一个状态,我们检查: 2.1。如果所需长度为2:我们将计数增加1,因为这是一个有效状态。 2.2。如果长度大于2,我们检查是否已经在memoization字典中计算了此状态。如果我们这样做了,我们从字典中提取计数值并使用它,否则我们会用起始状态和想要的序列长度递归地调用这个函数。

计时

当运行此函数时禁用回忆录,我们将获得N=200

代码语言:javascript
复制
%timeit count_seq_slow(200)
199 ms ± 9.63 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit count_seq(200)
13.6 ms ± 833 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

当增加到N=2000时,我们得到:

代码语言:javascript
复制
%timeit count_seq_slow(2000)
3.54 s ± 374 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit count_seq(2000)
147 ms ± 7.02 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
票数 3
EN

Stack Overflow用户

发布于 2022-04-07 07:53:22

K[n, d1, d2]是长度为n的有效序列数,如果d1,d2出现在前面,则序列仍然有效。或等效地,以d1,d2开头的长度为d1的有效序列的数目。

有一个K满足的递归关系:

代码语言:javascript
复制
K[n, d1, d2] = 0 if d1=d2 or d1 is not coprime to d2.
K[0, d1, d2] = 1
K[n, d1, d2] = sum(K[n-1, d2, d] for d=1..6 - {d1, d2})

这个K可以使用自下而上的动态程序或回忆录来计算.

对于n>=2,可以使用以下方法解决最初的问题:

代码语言:javascript
复制
S[n] = sum(K[n-2, d1, d2] for d1=1..6, d2=1..6)

对于n<=2,我们有S[0] = 1S[1] = 6

使用O(1)空间和O(n)时间编写这段代码的一种方法是:

代码语言:javascript
复制
from math import gcd

def S(n):
    if n == 0: return 1
    if n == 1: return 6
    K = [d1!=d2 and gcd(d1+1, d2+1)==1 for d1 in range(6) for d2 in range(6)]
    for _ in range(n-2):
        K = [0 if d1==d2 or gcd(d1+1, d2+1)!=1 else sum(K[d2*6+d] for d in range(6) if d!= d1 and d!=d2) for d1 in range(6) for d2 in range(6)]
    return sum(K)

这将迭代地从K[n,_,_]中计算K[n-1,_,_]

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71776343

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档