Python 计算小数表示形式中具有升序连续数字的范围内的数字
如果整数在其十进制表示中至少有三个升序连续数字,则它是一个特殊数字 连续顺序中的三位数字必须大于零 例如:Python 计算小数表示形式中具有升序连续数字的范围内的数字,python,algorithm,Python,Algorithm,如果整数在其十进制表示中至少有三个升序连续数字,则它是一个特殊数字 连续顺序中的三位数字必须大于零 例如:123,45123,245789,123456789,12345是特殊的数字,012,1012,1245是非特殊的数字 我必须计算在给定的S到E之间存在的特殊数字的数量。(1 是否存在任何此类解决方案 这样的解决方案当然存在,并且可以有效地计算s和E之间的特殊数字,即使s和E比10**18大得多。例如,我们将在下面看到确切的2685401982839147497953997698765007
123
,45123
,245789
,123456789
,12345
是特殊的
数字,012
,1012
,1245
是非特殊的
数字
我必须计算在给定的S
到E
之间存在的特殊数字的数量。(1
是否存在任何此类解决方案
这样的解决方案当然存在,并且可以有效地计算s
和E
之间的特殊数字,即使s
和E
比10**18
大得多。例如,我们将在下面看到确切的26854019828391474979539976987650077000644049724
特殊数字rs小于10**50
,因此所有50位数字中约有四分之一是特殊数字
我将概述解决方案的想法,然后给出一些Python代码
首先是简单的部分:对于任何非负整数n
,能够计算范围(n)
中的特殊数字就足够了。也就是说,我们只需要一个函数:
def count_special_below(n: int) -> int:
"""
Return count of special numbers m satisfying 0 <= m < n.
"""
# See below for implementation
def count_special_between(start: int, end: int) -> int:
"""
Return count of special numbers m satisfying start <= m <= end.
"""
return count_special_below(end + 1) - count_special_below(start)
def count_special_below(n: int) -> int:
"""
Return count of special numbers m satisfying 0 <= m < n.
"""
n_counts, n_cls = [0] * 16, 0
for digit in map(int, str(n)):
m_counts = [0] * 16
for tc, count in zip(transitions, n_counts):
for tcs in tc:
m_counts[tcs] += count
for t in transitions[n_cls][:digit]:
m_counts[t] += 1
n_counts, n_cls = m_counts, transitions[n_cls][digit]
return n_counts[15]
想法
我们将非负整数分成几个类。其中一类是特殊数,其余的是各种形式的非特殊数。除法的方式是,我们知道当我们添加一个给定的数字时,任何给定整数的类是如何变化的
具体而言,我们定义了16个互斥类,编号从0到15,如下所示:
- 类15包含所有特殊编号
- 类别14包含以数字“78”结尾的所有非特殊数字
- 类13包含以数字“67”结尾的所有非特殊数字
- 类别12包含以数字“56”结尾的所有非特殊数字
- 类11包含以数字“45”结尾的所有非特殊数字
- 类别10包含以数字“34”结尾的所有非特殊数字
- 类别9包含以数字“23”结尾的所有非特殊数字
- 第8类包含以数字“12”结尾的所有非特殊数字
- 类7包含所有以“7”结尾的非特殊数字,但不包含“67”
- 第6类包含所有以“6”结尾的非特殊数字,但不包括“56”
- 第5类包含所有以“5”结尾的非特殊数字,但不包括“45”
- 类别4包含所有以“4”结尾的非特殊数字,但不包含“34”
- 类别3包含所有以“3”结尾的非特殊数字,但不包含“23”
- 类别2包含以“2”结尾的所有非特殊数字,但不包括“12”
- 类1包含以“1”结尾的所有非特殊数字
- 类0包含以“8”、“9”或“0”结尾的所有非特殊数字,但不以“78”结尾
q
和r
的n=10*q=r
,那么我们可以计算n
的类,只知道q
的类和r
的值:我们不需要知道q
本身
例如,如果q
在类9
和r=4
,那么n=10*q+r
在类15中:这是特殊的。而如果q
在类9
和r=5
,那么n
在类5
,如果q
在类9
和r=0>n
在类0
中
我们可以通过所有160个(类、数字)组合来计算转换,然后将这些转换记录在一个简单的列表中
现在我们可以将其放大:再次假设n=10*q+r
,并且我们已经(例如递归地)计算了范围(q)
中属于16个类中每一类的整数的数量。然后我们可以使用转移矩阵来计算范围(n)中的整数的数量
属于16类中的每一类
例如,如果n=135457
,q=13545
和r=7
,则range(q)
中类9
的值的数量变成121
。这些121
值中的每一个值都恰好为range(135457)贡献一个特殊数字
,通过添加4
。还有135
类8
的值,它们同样在范围(135457)
中贡献一个特殊的数字。通过添加贡献,并注意考虑范围范围(135450135457)
,我们得到范围(135457)
中每个类的总计数
Python代码
这是迭代形式的代码。递归地表达它比迭代地表达更自然,但是对于非常大的起始值和结束值(例如有数千位数字),我们会遇到Python的递归限制
首先是转换矩阵:这是一个列表列表,行对应于16个类,列对应于数字。如果q
具有classc
,则10*q+r
具有class转换[c][r]
transitions = [
[0, 1, 2, 3, 4, 5, 6, 7, 0, 0],
[0, 1, 8, 3, 4, 5, 6, 7, 0, 0],
[0, 1, 2, 9, 4, 5, 6, 7, 0, 0],
[0, 1, 2, 3, 10, 5, 6, 7, 0, 0],
[0, 1, 2, 3, 4, 11, 6, 7, 0, 0],
[0, 1, 2, 3, 4, 5, 12, 7, 0, 0],
[0, 1, 2, 3, 4, 5, 6, 13, 0, 0],
[0, 1, 2, 3, 4, 5, 6, 7, 14, 0],
[0, 1, 2, 15, 4, 5, 6, 7, 0, 0],
[0, 1, 2, 3, 15, 5, 6, 7, 0, 0],
[0, 1, 2, 3, 4, 15, 6, 7, 0, 0],
[0, 1, 2, 3, 4, 5, 15, 7, 0, 0],
[0, 1, 2, 3, 4, 5, 6, 15, 0, 0],
[0, 1, 2, 3, 4, 5, 6, 7, 15, 0],
[0, 1, 2, 3, 4, 5, 6, 7, 0, 15],
[15, 15, 15, 15, 15, 15, 15, 15, 15, 15],
]
下面是完整的count\u special\u
功能:
def count_special_below(n: int) -> int:
"""
Return count of special numbers m satisfying 0 <= m < n.
"""
# See below for implementation
def count_special_between(start: int, end: int) -> int:
"""
Return count of special numbers m satisfying start <= m <= end.
"""
return count_special_below(end + 1) - count_special_below(start)
def count_special_below(n: int) -> int:
"""
Return count of special numbers m satisfying 0 <= m < n.
"""
n_counts, n_cls = [0] * 16, 0
for digit in map(int, str(n)):
m_counts = [0] * 16
for tc, count in zip(transitions, n_counts):
for tcs in tc:
m_counts[tcs] += count
for t in transitions[n_cls][:digit]:
m_counts[t] += 1
n_counts, n_cls = m_counts, transitions[n_cls][digit]
return n_counts[15]
但是,我们的新功能也可以很好地处理大量数据:
>>> count_special_between(0, 10**50)
26854019828391474979539976989876500770006474049724
在我的机器上,即使是计算低于10**10000
的特殊数字也只需要一两秒钟,但由于答案正好是10000
位(毫不奇怪,因为“大多数”大数字都是特殊的),我就不在这里了
与正则表达式的连接
众所周知,通过正则表达式进行匹配(在stri
>>> from greenery.lego import parse
>>> p = parse("[0-9]*(123|234|345|456|567|678|789)[0-9]*")
>>> p.to_fsm()
fsm(
alphabet = {'2', '1', '7', anything_else, '0', '4', '8', '3', '5', '6', '9'},
states = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
initial = 0,
finals = {15}, map = {
0: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 0, '9': 0},
1: {'0': 0, '1': 1, '2': 8, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 0, '9': 0},
2: {'0': 0, '1': 1, '2': 2, '3': 9, '4': 4, '5': 5, '6': 6, '7': 7, '8': 0, '9': 0},
3: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 10, '5': 5, '6': 6, '7': 7, '8': 0, '9': 0},
4: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 11, '6': 6, '7': 7, '8': 0, '9': 0},
5: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 12, '7': 7, '8': 0, '9': 0},
6: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 13, '8': 0, '9': 0},
7: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 14, '9': 0},
8: {'0': 0, '1': 1, '2': 2, '3': 15, '4': 4, '5': 5, '6': 6, '7': 7, '8': 0, '9': 0},
9: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 15, '5': 5, '6': 6, '7': 7, '8': 0, '9': 0},
10: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 15, '6': 6, '7': 7, '8': 0, '9': 0},
11: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 15, '7': 7, '8': 0, '9': 0},
12: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 15, '8': 0, '9': 0},
13: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 15, '9': 0},
14: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 0, '9': 15},
15: {'0': 15, '1': 15, '2': 15, '3': 15, '4': 15, '5': 15, '6': 15, '7': 15, '8': 15, '9': 15}})