问题是在给定的约束下找到有效序列 (A1,A2...AN) 的数量:
- Ai 的值介于 1 和 6 之间(1<= Ai <= 6)
- 两个相邻的数字应该是互质的。对于例如 2,4,.. 序列是不允许的
- 如果该值在序列中重复,则它们的索引差异应大于 2。对于例如如果 K=4,(1,2,3,1) 是有效的序列 while (1,3,4,3) 不是有效序列
注:N 在问题陈述中给出。
我只能想到一个回溯解决方案,其中每个递归调用都会针对给定的 Ai 位置尝试从 1 到 6 的所有数字。
这看起来更像是暴力方式。
请您帮忙提供一个最佳解决方案。
The question is to find number of valid sequences (A1,A2....AN) with given constraints:
- Value of Ai lies between 1 and 6 (1<= Ai <= 6)
- Two adjacent numbers should be coprime. For e.g. 2,4,.. sequence is not allowed
- If the value repeats in the sequence, then difference in their index should be greater than 2. For e.g. If K=4, (1,2,3,1) is a valid sequence while (1,3,4,3) is not a valid sequence
Note: N is given in the problem statement.
I could only think of a backtracking solution wherein each recursive call would try all numbers from 1 to 6 for the given Ai position.
This look like more of brute force way.
Could you please help with an optimal solution.
发布评论
评论(4)
我们可以使用这样一个事实,即只能使用6个可能的数字来构建数字。
dp [i] [j] [k]
,它基本上是i-digit数字的计数,i-th Digit作为j,(i-1)数字作为k
。{2:[1,3,5],3:[1,2,4,5],6:[1,5]} ...
dp [0] = 0对于所有j,k
,dp [1] = 0对于所有J,k
,dp [2] [j] [j] [k] = 1,如果1 gcd(j,k)== 1和j!= k
sum(dp [n] [1..6])
这具有
o(n*6*6)的时间和空间复杂性
〜o(n)
。编辑:@kellybundy很友好,可以添加Python实现;纠正了它(在我的回答中是一个小缺点),并在此处添加:
We can use the fact that only 6 possible digits can be used to construct numbers.
dp[i][j][k]
, which is basicallycount of i-digit numbers with the i-th digit as j, (i-1)th digit as k
.{2: [1,3,5], 3: [1,2,4,5], 6: [1,5]}...
dp[0] = 0 for all j,k
,dp[1] = 0 for all j,k
,dp[2][j][k] = 1 if gcd(j,k)==1 and j!=k
sum(dp[N][1..6])
This has a time and space complexity of
O(N*6*6)
~O(N)
.Edit: @KellyBundy was kind enough to add a Python implementation; corrected it (and a minor flaw in my answer) and added here:
令 K[n, d1, d2] 为长度为 n 的有效序列的数量,这样如果 d1、d2 恰好出现在前面,则该序列仍然有效。或者等效地,以 d1、d2 开头的长度为 n+2 的有效序列的数量。
K
满足一个递推关系:这个
K
可以使用自下而上的动态程序或记忆法来计算。对于
n>=2
可以解决原始问题,使用以下方法:对于
n<=2
,我们有S[0] = 1
和S[1] = 6
。使用 O(1) 空间和 O(n) 时间编写此代码的一种方法是:
从
K[n-1, _,_]
。Let
K[n, d1, d2]
be the number of valid sequences of length n such that the sequence remains valid if d1, d2 appear just before. Or equivalently, the number of valid sequences of length n+2 that start with d1, d2.There's a recurrence relation that
K
satisfies:This
K
can be calculated using a bottom-up dynamic program, or memoization.The original problem can be solved for
n>=2
, using this:For
n<=2
, we haveS[0] = 1
andS[1] = 6
.One way to write this code, using O(1) space and O(n) time is this:
This iteratively computes
K[n,_,_]
fromK[n-1,_,_]
.这里有一个图形搜索问题,可以使用回溯搜索来解决,并且可以使用 记忆。
定义状态
遵循问题中的规则,状态是元组,其中元组中的第一个值是当前数字
a_n
和系列中的前一个数字a_{n-1}< /code> 元组中的第二个值是序列的长度,因为数字的出现可以在
2
或更大的间隔内。此外,禁止状态是两个数字不互质的状态,这意味着
长度为 2 的每个排列都是有效状态,但禁止集合除外:
示例:
((2,3), 5)
处于状态 (2,3),所需序列长度为 5。在这种情况下,后面的数字不能是 2,3(以保持距离至少 2) 或 6 (以保持相邻数字互质),因此总共有以下状态:构建解决方案
我将提供的解决方案是图的递归 DFE,具有记忆功能以实现时间优化。解决方案如下:
说明
如果所需长度为 1,则返回 6,因为任一数字都可以位于第一个位置。
我们看看起始状态是否为
None
,我们假设这是初始调用,因此我们创建长度为 2 的数字的所有可能的无禁止排列。否则,我们创建一个集合所有可能的下一个状态。对于每个可能的下一个状态,我们检查:
2.1。如果所需的长度为 2:我们将计数增加 1,因为这是有效状态
2.2。如果长度大于 2,我们检查之前是否已经在
memoization
字典中计算过这个状态。如果我们这样做了,我们会从字典中提取计数值并使用它,否则我们会使用起始状态和所需的序列长度递归地调用该函数。计时
在禁用记忆功能的情况下运行此函数时,我们得到
N=200
:当增加到
N=2000
时,我们得到:What you have here is a graph search problem which can be solved using backtracking search and can be made to run even faster using memoization.
Defining the states
Following the rules in the question, the states are tuple where the first value in the tuple are the current number
a_n
and the previous number in the seriesa_{n-1}
and the second value in the tuple is the length of the sequence, since the occurance of a number can be in an interval of2
or more.In addition, the forbidden states are states where the two numbers are not co-prime, this means that every permutation of the
with the length of 2 is a valid state except the forbidden set which is:
Example:
((2,3), 5)
is in state (2,3) with a needed sequence length of 5. In this case the following number can not be 2,3 (to keep distance of at least 2) or 6 (to keep adjacent numbers co-prime) so a total of thee subsequent states:Building the solution
The solution I will offer is a recursive DFE of the graph with memoization for time optimization. the solution is as follows:
Explenation
If the wanted length is 1, reutn 6 since either of the numbers can be in the 1st location.
We see if the start state is
None
we assume that this is the initial calling, and so we create all the possible none forbidden permutations of the numbers with length 2. otherwise, we create a set of all the possible next states.For each possible next state, we check:
2.1. if the length required is 2: we increase the count by 1 since this is a valid state
2.2. If the length is more than 2, we check is we already computed this state before in the
memoization
dictionary. If we did we pull the count value from the dictionary and use that, otherwise we call the function recursively with the starting state and the wanted sequence length.Timing
When running this function with memoization disabled we get for
N=200
:When increasing to
N=2000
we get:接下来可以出现什么数字仅取决于前两个数字。因此,这里有一个迭代解决方案,仅保留 O(1) 个数字,对于 N=2000,它的速度大约是 Tomer 的两倍(即使没有任何优化,我什至一直调用
gcd
):My < code>ctr 是一个哈希映射,其键是代表最后两个数字的对,值表明有多少有效序列以这两个数字结尾。使用
(7, 7)
初始化,因为它允许所有扩展。为了获得乐趣和最大性能,对所有状态和转换进行硬编码,这比我上面的 N=2000 解决方案快了大约 14 倍,并在大约 10 秒内解决了 N=100,000(结果是一个具有 45,077 位数字的数字):
这里,例如
x23
是以数字 2 和 3 结尾的有效序列的数量。还有进一步微优化的空间(使用额外的y
变量在x
和y
之间交替,而不是构建+解包元组,或利用常见的子和,例如x21+x41
>,使用了三次),但我就到此为止吧。哦,实际上...正如人们在斐波那契数中看到的那样,我们可以将转换表示为 22×22 矩阵,然后通过平方求幂快速计算该矩阵的 N 次(或 N-2 次)幂。那应该更快。
嗯...现在实现了矩阵幂方法,遗憾的是它慢。我想它只有在您只需要某种截断值(例如仅前 100 位或后 100 位数字和数字位数,或对某个数字取模的序列数)时才会有帮助。否则,虽然矩阵幂方法确实需要较少的运算,但它们包括大数的乘法,而不仅仅是加法,后者速度较慢。无论如何,这是我的实现(在线尝试!):
作为演示,这里有一个计算最后三位数字的修改。对于 N=100,000,大约需要 0.26 秒,对于 N=1,000,000,000,000,000,大约需要 1 秒。
What number can come next only depends on the previous two numbers. So here's an iterative solution that only keeps O(1) numbers around, it's about twice as fast as Tomer's for N=2000 (even without any optimizations, I even call
gcd
all the time):My
ctr
is a hash map whose keys are pairs representing the last two numbers, and the values tell how many valid sequences end in those two numbers. Initialized with(7, 7)
because that allows all extensions.For fun and max performance, hardcoding all states and transitions, this is about 14 times faster than my above solution for N=2000 and solves N=100,000 in about 10 seconds (the result is a number with 45,077 digits):
Here, for example
x23
is the number of valid sequences ending in the numbers 2 and 3. There's room for further microoptimizations (using additionaly
variables to alternate betweenx
andy
instead of building+unpacking tuples, or exploiting common subsums likex21+x41
, which is used three times), but I'll leave it at this.Oh, actually... as one might've seen with Fibonacci numbers, we can represent the transitions as a 22×22 matrix and then quickly compute the Nth (or N-2th) power of that matrix with exponentiation by squaring. That should be even much faster.
Meh... implemented the matrix power method now, and sadly it's slower. I guess it really only helps if you only need some kind of truncated values, like only the first or last 100 digits and the number of digits, or the number of sequences modulo some number. Otherwise, while the matrix power method does need fewer operations, they include multiplications of large numbers instead of just additions, which is slower. Anyway, here's my implementation (Try it online!):
As demonstration, here's a modification that computes the last three digits. For N=100,000 it takes about 0.26 seconds and for N=1,000,000,000,000,000 it takes about one second.