파이썬 2.7
이 방법은 다음 고려 사항을 이용합니다.
모든 정수는 2의 거듭 제곱의 합계로 표시 될 수 있습니다. 2의 거듭 제곱의 지수는 2의 거듭 제곱으로 나타낼 수도 있습니다. 예를 들면 다음과 같습니다.
8 = 2^3 = 2^(2^1 + 2^0) = 2^(2^(2^0) + 2^0)
우리가 끝내는 이러한 표현식은 세트 세트로 표현할 수 있습니다 (Python에서는 내장을 사용했습니다 frozenset
).
0
빈 세트가 {}
됩니다.
2^a
를 나타내는 세트를 포함하는 세트가 a
됩니다. 예 : 1 = 2^0 -> {{}}
및 2 = 2^(2^0) -> {{{}}}
.
a+b
대표 세트의 연결하게 a
하고 b
. 예 :3 = 2^(2^0) + 2^0 -> {{{}},{}}
2^2^...^2
숫자 값이 너무 커서 정수로 저장하기 어려운 경우에도 양식의 표현식을 고유 한 세트 표현으로 쉽게 변환 할 수 있습니다.
의 경우 n=20
이것은 내 컴퓨터의 CPython 2.7.5 에서 8.7s로 실행됩니다 (PyPy에서는 조금 느리고 PyPy에서는 훨씬 느립니다).
"""Analyze the expressions given by parenthesizations of 2^2^...^2.
Set representation: s is a set of sets which represents an integer n. n is
given by the sum of all 2^m for the numbers m represented by the sets
contained in s. The empty set stands for the value 0. Each number has
exactly one set representation.
In Python, frozensets are used for set representation.
Definition in Python code:
def numeric_value(s):
n = sum(2**numeric_value(t) for t in s)
return n"""
import itertools
def single_arg_memoize(func):
"""Fast memoization decorator for a function taking a single argument.
The metadata of <func> is *not* preserved."""
class Cache(dict):
def __missing__(self, key):
self[key] = result = func(key)
return result
return Cache().__getitem__
def count_results(num_exponentiations):
"""Return the number of results given by parenthesizations of 2^2^...^2."""
return len(get_results(num_exponentiations))
@single_arg_memoize
def get_results(num_exponentiations):
"""Return a set of all results given by parenthesizations of 2^2^...^2.
<num_exponentiations> is the number of exponentiation operators in the
parenthesized expressions.
The result of each parenthesized expression is given as a set. The
expression evaluates to 2^(2^n), where n is the number represented by the
given set in set representation."""
# The result of the expression "2" (0 exponentiations) is represented by
# the empty set, since 2 = 2^(2^0).
if num_exponentiations == 0:
return {frozenset()}
# Split the expression 2^2^...^2 at each of the first half of
# exponentiation operators and parenthesize each side of the expession.
split_points = xrange(num_exponentiations)
splits = itertools.izip(split_points, reversed(split_points))
splits_half = ((left_part, right_part) for left_part, right_part in splits
if left_part <= right_part)
results = set()
results_add = results.add
for left_part, right_part in splits_half:
for left in get_results(left_part):
for right in get_results(right_part):
results_add(exponentiate(left, right))
results_add(exponentiate(right, left))
return results
def exponentiate(base, exponent):
"""Return the result of the exponentiation of <operands>.
<operands> is a tuple of <base> and <exponent>. The operators are each
given as the set representation of n, where 2^(2^n) is the value the
operator stands for.
The return value is the set representation of r, where 2^(2^r) is the
result of the exponentiation."""
# Where b is the number represented by <base>, e is the number represented
# by <exponent> and r is the number represented by the return value:
# 2^(2^r) = (2^(2^b)) ^ (2^(2^e))
# 2^(2^r) = 2^(2^b * 2^(2^e))
# 2^(2^r) = 2^(2^(b + 2^e))
# r = b + 2^e
# If <exponent> is not in <base>, insert it to arrive at the set with the
# value: b + 2^e. If <exponent> is already in <base>, take it out,
# increment e by 1 and repeat from the start to eventually arrive at:
# b - 2^e + 2^(e+1) =
# b + 2^e
while exponent in base:
base -= {exponent}
exponent = successor(exponent)
return base | {exponent}
@single_arg_memoize
def successor(value):
"""Return the successor of <value> in set representation."""
# Call exponentiate() with <value> as base and the empty set as exponent to
# get the set representing (n being the number represented by <value>):
# n + 2^0
# n + 1
return exponentiate(value, frozenset())
def main():
import timeit
print timeit.timeit(lambda: count_results(20), number=1)
for i in xrange(21):
print '{:.<2}..{:.>9}'.format(i, count_results(i))
if __name__ == '__main__':
main()
(메모 레이션 데코레이터의 개념은 http://code.activestate.com/recipes/578231- 아마도 가장 빠른 -memoization-decorator-in-the- /에서 복사되었습니다 .)
산출:
8.667753234
0...........1
1...........1
2...........1
3...........2
4...........4
5...........8
6..........17
[...]
19.....688366
20....1619087
다른 타이밍 n
:
n time
16 0.240
17 0.592
18 1.426
19 3.559
20 8.668
21 21.402
모든 n
내 컴퓨터의 메모리 오류가 위의 21 개 결과.
누군가 다른 언어로 번역하여 더 빨리 만들 수 있다면 관심이 있습니다.
편집 :get_results
기능을 최적화했습니다 . 또한 2.7.2 대신 Python 2.7.5를 사용하면 조금 더 빠르게 실행됩니다.
2^n
이기 때문에 덧셈과 곱셈을 독점적으로 사용할 수 있어야 하므로을 제외한 것을 추적 할 필요가 없습니다n
. 즉, 지수 규칙을 사용하는 것이 현명 해 보입니다. 그러나이 작업을 수행하는 데 더 똑똑하고 완전히 대수적인 방법이 있습니다.