2 ^ 2 ^… ^ 2의 괄호로 묶을 수있는 가능한 숫자 결과 수


19

연산자를 사용한 표현식 2^2^...^2을 고려하십시오 . 연산자 는 지수 ( "의 거듭 제곱")를 의미합니다. 디폴트 연관성이 없다고 가정하므로, 표현은 명확하게 괄호로 묶어야 모호하지 않게됩니다. 표현식을 괄호로 묶는 방법의 수는 카탈로니아 어 숫자로 표시 됩니다.n^^ C_n=(2n)!/(n+1)!/n!

예를 들어 (2^2)^(2^2)=((2^2)^2)^2, 다른 괄호는 같은 수치 결과를 제공하기 때문에 주어진 수치에 대한 다른 수치 결과 는 모두 n보다 적습니다 . 카탈로니아 어 숫자가 아닌 순서가 시작됩니다.C_nn>11, 1, 2, 4, 8, ...1, 2, 5, 14, 42, ...

문제는, 허용 빠른 프로그램 (또는 함수) 작성하는 n입력으로서 상기 식의 다른 가능한 수치 결과의 수를 반환 2^2^...^2n사업자 ^. n발전 함에 따라 성능이 크게 저하되지 않아야 하므로 고전력 타워를 직접 계산하는 것은 좋지 않습니다.


나는 여기서 아이디어를 공유하고 있지만 대답은 항상 형식 2^n이기 때문에 덧셈과 곱셈을 독점적으로 사용할 수 있어야 하므로을 제외한 것을 추적 할 필요가 없습니다 n. 즉, 지수 규칙을 사용하는 것이 현명 해 보입니다. 그러나이 작업을 수행하는 데 더 똑똑하고 완전히 대수적인 방법이 있습니다.
하기 Fors

내가 생각 @Fors는 n것입니다 여전히 컴퓨팅에 너무 큰. 아직도, 잘 지적했다. "1 또는 2 ^ (...) 또는 (...) + (...)"형식의 재귀 표현 일 수 있습니다. 그러나 여전히 숫자 표현을 정규화하는 방법에 대한 문제가 있습니다 (또는 값 평등에 대해 두 표현을 비교하십시오).
John Dvorak

4
@JanDvorak, A002845 (폐쇄 된 양식 없음)
Peter Taylor


1
@ Vladimir Reshetnikov : 공식에 하나의 오류가 있다고 생각합니다. n두 개가 C_n=(2n)!/(n+1)!/n!있고 괄호 수 여야하는 경우 n = 3의 경우 5 여야합니다. 맞습니까? 내가보고 (2^2)^2하고 2^(2^2)있지만, 다른 세 조합은 무엇인가? C_n은 n + 1 2에 대한 괄호 수를 제공한다고 생각합니다.
마틴 토마

답변:


9

파이썬 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를 사용하면 조금 더 빠르게 실행됩니다.


C # 번역을 만들었지 만 정렬 된 배열을 사용하고 세트가 아닌 순서대로 추가를 수행하면 검사가 포함됩니다. 속도가 훨씬 느리고 아직 후속 기능을 기억하지 못하거나 비교 비용으로 인한 것인지 여부를 확인하지 못했습니다.
피터 테일러

1
@flornquake의 (화려한) 코드를 프로파일 링하지는 않았지만 CPU 시간의 대부분이 유비쿼터스 해시 테이블과 해시 키를 사용하여 Python에서 상당히 최적화 된 멤버쉽 테스트 및 세트 조작 작업을 수행하는 데 소비되는 것으로 가정합니다. 루틴. 이와 같은 지수 알고리즘을 사용하면 메모 화는 확실히 큰 일입니다. 이를 생략하면 성능이 기하 급수적으로 느려질 수 있습니다.
Tobia

@Tobia, 실제로 C #에서 후속 기능을 기억하는 것이 느리다는 것을 알았습니다. 또한 더 많은 리터럴 번역 (세트 연산 사용)이 하위 레벨 추가보다 상당히 느리다는 것을 알았습니다. 원래 코드에서 찾은 유일한 개선 사항은을 고려하는 것이 었으며 (a^b)^c = (a^c)^b여전히이 Python 구현보다 훨씬 느립니다.
피터 테일러

@PeterTaylor : 편집 : 내가 볼 수있는 한, flornquake의 알고리즘은 나무가 나무 자체의 집합 인 트리 집합을 만드는 것에 의존합니다. 가장 작은 빈 세트에서 가장 큰 세트 세트에 이르기까지이 트리의 모든 부분이 메모됩니다. 즉,이 모든 트리에는 CPU에 의해 한 번만 계산되고 RAM에 한 번만 저장된 "반복 된 구조"가 포함됩니다. "순서대로 추가"알고리즘이이 반복 구조를 모두 식별하고 한 번 계산하고 있습니까? (위에서 지수 복잡성이라고 부른 것) en.wikipedia.org/wiki/Dynamic_programming
Tobia

@Tobia, 우리는 겹쳤다. 코드를 게시했습니다.
피터 테일러

5

씨#

이것은 직접 번역보다 약간의 속도 향상을 제공하는 하위 수준 추가 루틴을 사용하여 flornquake의 Python 코드를 C #으로 변환 한 것입니다. 내가 가지고있는 가장 최적화 된 버전은 아니지만 값뿐만 아니라 트리 구조를 저장해야하기 때문에 꽤 길다.

using System;
using System.Collections.Generic;
using System.Linq;

namespace Sandbox {
    class PowerTowers {
        public static void Main() {
            DateTime start = DateTime.UtcNow;
            for (int i = 0; i < 17; i++)
                Console.WriteLine("{2}: {0} (in {1})", Results(i).Count, DateTime.UtcNow - start, i);
        }

        private static IList<HashSet<Number>> _MemoisedResults;

        static HashSet<Number> Results(int numExponentations) {
            if (_MemoisedResults == null) {
                _MemoisedResults = new List<HashSet<Number>>();
                _MemoisedResults.Add(new HashSet<Number>(new Number[] { Number.Zero }));
            }

            if (numExponentations < _MemoisedResults.Count) return _MemoisedResults[numExponentations];

            HashSet<Number> rv = new HashSet<Number>();
            for (int i = 0; i < numExponentations; i++) {
                IEnumerable<Number> rhs = Results(numExponentations - 1 - i);
                foreach (var b in Results(i))
                    foreach (var e in rhs) {
                        if (!e.Equals(Number.One)) rv.Add(b.Add(e.Exp2()));
                    }
            }
            _MemoisedResults.Add(rv);
            return rv;
        }
    }

    // Immutable
    struct Number : IComparable<Number> {
        public static Number Zero = new Number(new Number[0]);
        public static Number One = new Number(Zero);

        // Ascending order
        private readonly Number[] _Children;
        private readonly int _Depth;
        private readonly int _HashCode;

        private Number(params Number[] children) {
            _Children = children;
            _Depth = children.Length == 0 ? 0 : 1 + children[children.Length - 1]._Depth;

            int hashCode = 0;
            foreach (var n in _Children) hashCode = hashCode * 37 + n.GetHashCode() + 1;
            _HashCode = hashCode;
        }

        public Number Add(Number n) {
            // "Standard" bitwise adder built from full adder.
            // Work forwards because children are in ascending order.
            int off1 = 0, off2 = 0;
            IList<Number> result = new List<Number>();
            Number? carry = default(Number?);

            while (true) {
                if (!carry.HasValue) {
                    // Simple case
                    if (off1 < _Children.Length) {
                        if (off2 < n._Children.Length) {
                            int cmp = _Children[off1].CompareTo(n._Children[off2]);
                            if (cmp < 0) result.Add(_Children[off1++]);
                            else if (cmp == 0) {
                                carry = _Children[off1++].Add(One);
                                off2++;
                            }
                            else result.Add(n._Children[off2++]);
                        }
                        else result.Add(_Children[off1++]);
                    }
                    else if (off2 < n._Children.Length) result.Add(n._Children[off2++]);
                    else return new Number(result.ToArray()); // nothing left to add
                }
                else {
                    // carry is the (possibly joint) smallest value
                    int matches = 0;
                    if (off1 < _Children.Length && carry.Value.Equals(_Children[off1])) {
                        matches++;
                        off1++;
                    }
                    if (off2 < n._Children.Length && carry.Value.Equals(n._Children[off2])) {
                        matches++;
                        off2++;
                    }

                    if ((matches & 1) == 0) result.Add(carry.Value);
                    carry = matches == 0 ? default(Number?) : carry.Value.Add(One);
                }
            }
        }

        public Number Exp2() {
            return new Number(this);
        }

        public int CompareTo(Number other) {
            if (_Depth != other._Depth) return _Depth.CompareTo(other._Depth);

            // Work backwards because children are in ascending order
            int off1 = _Children.Length - 1, off2 = other._Children.Length - 1;
            while (off1 >= 0 && off2 >= 0) {
                int cmp = _Children[off1--].CompareTo(other._Children[off2--]);
                if (cmp != 0) return cmp;
            }

            return off1.CompareTo(off2);
        }

        public override bool Equals(object obj) {
            if (!(obj is Number)) return false;

            Number n = (Number)obj;
            if (n._HashCode != _HashCode || n._Depth != _Depth || n._Children.Length != _Children.Length) return false;
            for (int i = 0; i < _Children.Length; i++) {
                if (!_Children[i].Equals(n._Children[i])) return false;
            }

            return true;
        }

        public override int GetHashCode() {
            return _HashCode;
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.