모든 부분 집합의 결과를 얻는 가장 빠른 알고리즘


23

n배열의 숫자 (정수라고 가정 할 수 없음)가 주어지면 size의 모든 하위 집합의 곱을 계산하고 싶습니다 n-1.

숫자가 0이 아닌 한 모든 숫자를 곱한 다음 각 숫자로 차례로 나눔으로써이 작업을 수행 할 수 있습니다. 그러나 부서없이 얼마나 빨리 할 수 ​​있습니까?

나누기를 허용하지 않으면 n-1 크기의 모든 부분 집합의 곱을 계산하는 데 필요한 최소 산술 연산 (예 : 곱셈 및 덧셈)은 얼마입니까?

분명히 (n-1)*n곱셈으로 할 수 있습니다 .

명확히하기 위해 출력은 n다른 제품이며 허용되는 메모리에 대한 읽기 및 쓰기 이외의 유일한 작업은 곱셈, 덧셈 및 뺄셈입니다.

입력은 세 개의 숫자가있는 경우 2,3,5, 출력은 세 개의 숫자이며 15 = 3*5, 10 = 2*5그리고 6 = 2*3.

승리 기준

답변은 코드에서 사용할 산술 연산 수에 대한 정확한 공식을 제공해야합니다 n. 인생을 단순하게 만들기 위해 n = 1000점수를 판단하기 위해 공식에 연결 합니다. 낮을수록 좋습니다.

코드에 대한 정확한 공식을 생성하기가 너무 어렵다면 코드를 실행 n = 1000하여 코드의 산술 연산을 계산할 수 있습니다. 그러나 정확한 공식이 가장 좋습니다.

n=1000쉽게 비교할 수 있도록 점수를 답에 추가해야합니다 .


4
1을 곱하면 무료로 계산할 수 있습니까? 그렇지 않으면 나는 이것을하는 커스텀 곱셈 함수를 정의 할 것입니다.
xnor

3
충분히 많은 스페이서 0 숫자와 함께 숫자를 연결하여 전체 곱셈을 병렬로 처리하는 것이 규칙에 위배됩니까?
xnor

1
같은 작업 함 +에서 지수 계산? 이 경우 배열 인덱싱도 계산됩니까? (추가 및 역 참조를위한 모든 구문 설탕 때문이다).
Nore

2
@nore OK 내가 줄 :) 어떤 식 으로든 입력과 관련된 산술 연산을 계산하십시오.
Arthur

1
분명히 당신은 그것을 할 수 (n-1)*n곱셈 당신 말은 (n-2)*n바로?
Luis Mendo 2016 년

답변:


25

파이썬, 3 (n-2) 연산, 점수 = 2994

l = list(map(float, input().split()))
n = len(l)

left = [0] * len(l)
right = [0] * len(l)
left[0] = l[0]
right[-1] = l[-1]
for i in range(1,len(l)-1):
  left[i] = l[i] * left[i - 1]
  right[-i-1] = l[-i-1] * right[-i]

result = [0] * len(l)
result[-1] = left[-2]
result[0] = right[1]
for i in range(1, len(l) - 1):
  result[i] = left[i - 1] * right[i+1]

print(result)

배열 leftright각각 왼쪽과 오른쪽에서 배열의 누적 곱을 포함합니다.

편집 : 우리가 곱셈 만 사용하는 경우 3 (n-2)가 n> = 2에 필요한 최적 연산 수임을 증명하십시오.

우리는 이것을 유도함으로써 그렇게 할 것입니다. 위의 알고리즘에 의해, 우리는 n> = 2에 대해, 3 (n-2)가 필요한 곱셈의 수의 하한임을 증명해야합니다.

n = 2의 경우 최소 0 = 3 (2-2) 곱셈이 필요하므로 결과는 간단합니다.

n> 2로하고 n-1 개의 요소에 대해 적어도 3 (n-3) 곱셈이 필요하다고 가정합니다. k 곱셈을 갖는 n 개의 요소에 대한 솔루션을 고려하십시오. 이제이 요소들의 마지막 요소를 1 인 것처럼 제거하고 모든 곱셈을 직접 단순화합니다. 우리는 또한 다른 모든 요소의 곱으로 이어지는 곱셈을 제거합니다. 분할이 허용되지 않기 때문에 다른 요소의 n-2 곱을 얻기 위해 중간 값으로 사용할 수 없기 때문에 필요하지 않기 때문입니다. 이것은 우리에게 l 곱셈과 n-1 원소에 대한 해결책을 남깁니다.

유도 가설에 의해 l> = 3 (n-3)입니다.

이제 곱셈이 얼마나 많이 제거되었는지 살펴 보겠습니다. 그중 하나는 마지막 요소를 제외한 모든 요소의 곱으로 이어지는 것입니다. 또한 마지막 요소는 최소한 두 번의 곱셈에 직접 사용되었습니다. 하나만 사용 된 경우 다른 요소의 일부 제품으로 구성된 중간 결과를 곱할 때 사용되었습니다. 일반성을 잃지 않고이 중간 결과에 제품의 첫 번째 요소가 포함되어 있다고 가정하겠습니다. 그런 다음 마지막 요소를 포함하는 모든 제품이 마지막 요소이거나 첫 번째 요소를 포함하므로 모든 요소의 곱을 얻을 수있는 방법은 없습니다.

따라서 우리는 k> = l + 3> = 3 (n-2)를 가지며, 주장 된 정리를 증명합니다.


8
이 밝혀 하스켈에서 매우 깨끗 : f l = zipWith (*) (scanl (*) 1 l) (scanr (*) 1 $ tail l).
xnor

의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Dennis

12

하스켈 , 2994 점

group :: Num a => [a] -> [[a]]
group (a:b:t) = [a,b] : group t
group [a] = [[a]]
group [] = []

(%) :: (Num a, Eq a) => a -> a -> a
a % 1 = a
1 % b = b
a % b = a * b

prod_one_or_two :: (Num a, Eq a) => [a] -> a
prod_one_or_two [a, b] = a % b
prod_one_or_two [x] = x

insert_new_value :: (Num a, Eq a) => ([a], a) -> [a]
insert_new_value ([a, b], c) = [c % b, c % a]
insert_new_value ([x], c) = [c]

products_but_one :: (Num a, Eq a) => [a] -> [a]
products_but_one [a] = [1]
products_but_one l = 
    do combination <- combinations ; insert_new_value combination
    where 
        pairs = group l
        subresults = products_but_one $ map prod_one_or_two pairs
        combinations = zip pairs subresults

온라인으로 사용해보십시오!

우리에게 목록이 있다고 가정 해보십시오 [a,b,c,d,e,f,g,h]. 먼저 쌍으로 묶습니다 [[a,b],[c,d],[e,f],[g,h]]. 그런 다음 pairs제품 의 절반 크기 목록 을 되풀이하여subresults

[a*b, c*d, e*f, g*h] -> [(c*d)*(e*f)*(g*h), (a*b)*(e*f)*(g*h), (a*b)*(c*d)*(g*h), (a*b)*(c*d)*(e*f)]

우리가 첫 번째 요소를 가지고가는 경우에 (c*d)*(e*f)*(g*h)곱셈 그것에 의해, 및 ba각각, 우리는하지만의 제품을 얻을 a모든하지만를 b. 모든 쌍에 대해이 작업을 수행하고 해당 쌍이 누락 된 재귀 결과를 얻으면 최종 결과가 나옵니다. 홀수 길이의 경우는 홀수 요소가 재귀 단계와 짝을 이루지 않고 처리되며 나머지 요소의 반환 된 제품은 그렇지 않은 제품입니다.

곱셈 횟수는 페어링 제품, 재귀 호출, 개별 요소가있는 제품에 대한 곱셈 t(n)입니다 . 이것은 홀수를 제공 합니다 . 기본 사례에 대해 홀수 및 무시 곱하기에 더 정확한 카운트를 사용 하면에 대한 점수 를 얻 습니다 .n/2t(n/2)nt(n) = 1.5 * n + t(n/2)nn12997n=1000


이거 좋은데.
Arthur

내 대답에서와 같이 점수가 2995가 아니라 2994가 아닌 이유는 두 숫자의 거듭 제곱뿐만 아니라 모든 숫자의 곱을 계산하기 때문에 나중에 잘립니다. 주의해서 다루면 products_but_one'올바른 길이의 무언가를 반환하여이를 피할 수 있습니다.
Nore

@ nore 기본 사례 1에 곱하기가 자유롭기 때문에 카운트에 여분의 곱셈이 있음을 알았습니다 . 패딩 1은 영향을 미치지 않았지만 알고리즘을 사용하지 않도록 정리했습니다.
xnor

이 코드는 입력이 정수라고 가정합니까?

@Lembik 옵션 타입 어노테이션에서만 가능합니다. 모두로 변경하겠습니다 float.
xnor

9

하스켈 , 9974 점

partition :: [Float] -> ([Float], [Float])
partition = foldr (\a (l1,l2) -> (l2, a:l1)) ([],[])

(%) :: Float -> Float -> Float
a % 1 = a
1 % b = b
a % b = a*b

merge :: (Float, [Float]) -> (Float, [Float]) -> (Float, [Float])
merge (p1,r1) (p2, r2) = (p1%p2, map(%p1)r2 ++ map(%p2)r1)

missing_products' :: [Float] -> (Float, [Float])
missing_products' [a] = (a,[1])
missing_products' l = merge res1 res2
    where
        (l1, l2) = partition l
        res1 = missing_products' l1
        res2 = missing_products' l2

missing_products :: [Float] -> [Float]
missing_products = snd . missing_products'

온라인으로 사용해보십시오!

병합 정렬을 매우 연상시키는 분할 및 정복 전략. 인덱싱을 수행하지 않습니다.

이 기능 partition은 파티션의 반대편에 교대로 요소를 배치하여 목록을 가능한 동일하게 반으로 나눕니다. (p,r)각 반쪽 의 결과 를 r누락 된 제품 목록 및 p전체 제품 과 재귀 적으로 병합합니다 .

전체 목록의 출력을 위해 누락 된 요소는 절반 중 하나에 있어야합니다. 해당 요소가 누락 된 제품은 절반에 해당하는 1 개의 누락 제품이며 다른 절반에 대한 전체 제품을 곱한 값입니다. 따라서 우리는 각 제품에 누락이있는 제품과 나머지 절반의 전체 제품을 곱하고 결과 목록을 다음과 같이 map(*p1)r2 ++ map(*p2)r1)만듭니다. n곱셈 이 필요 n합니다. 길이는 어디 입니까? 우리는 또한 p1*p2나중에 사용할 수 있도록 새로운 정식 제품을 만들어야하는데 1 배 더 많은 비용이 든다.

이 작업의 수에 대한 일반적인 재귀 제공 t(n)과 함께 n도를 : t(n) = n + 1 + 2 * t(n/2). 홀수는 비슷하지만 하위 목록 중 하나가 1더 큽니다. n*(log_2(n) + 1)홀수 / 짝수 구분이 정확한 값에 영향을 주지만 재귀를 수행하면 곱셈 을 얻습니다 . 최대 값은하기 t(3)에 의해 승산되지 향상되는 1변형에 의해 정의 (%)(*)해당 바로 가기 _*1또는 1*_케이스.

이것은에 대한 9975곱셈을 제공 합니다 n=1000. Haskell의 게으름은 외부 레이어에서 사용되지 않은 전체 제품이 계산되지 않는다는 것을 의미합니다 9974. 내가 실수하면 명시 적으로 생략 할 수 있습니다.


1 분 전에 타임 스탬프로 날 이겼어
Nore

수식을 정확하게 계산하기가 어려운 경우 수식을 실행 n = 1000하고 코드의 산술 연산을 계산하십시오.
Arthur

우리의 코드는 기본적으로 동일하기 때문에 당신이 있었는지, 이해가 안 9974하지 9975에 대한 곱셈 n = 1000(외부 층의 전체 제품을 컴퓨팅의 경우)를. 1테스트에 사용한 입력에 a를 포함 시켰습니까?
Nore

@ nore 당신 말이 맞아요 곱셈 함수 호출 횟수에 대한 재귀를 수행하는 코드를 작성했습니다. 직접 전화를 계산하는 것이 더 안정적 일 것입니다. 하스켈에서 어떻게해야하는지 아는 사람이 있습니까?
xnor

1
@xnor 사용할 수 trace에서 Debug.Trace포괄와 | trace "call!" False = undefined가드, 나는 생각한다. 그러나 이것은 unsafePerformIO후드 아래에서 사용 되므로 실제로 그렇게 많이 개선되지는 않습니다.
Soham Chowdhury

6

하스켈 , 2994 점

group :: [a] -> Either [(a, a)] (a, [(a, a)])
group [] = Left []
group (a : l) = case group l of
  Left pairs -> Right (a, pairs)
  Right (b, pairs) -> Left ((a, b) : pairs)

products_but_one :: Num a => [a] -> [a]
products_but_one [_] = [1]
products_but_one [a, b] = [b, a]
products_but_one l = case group l of
  Left pairs ->
    let subresults =
          products_but_one [a * b | (a, b) <- pairs]
    in do ((a, b), c) <- zip pairs subresults; [c * b, c * a]
  Right (extra, pairs) ->
    let subresult : subresults =
          products_but_one (extra : [a * b | (a, b) <- pairs])
    in subresult : do ((a, b), c) <- zip pairs subresults; [c * b, c * a]

온라인으로 사용해보십시오!

작동 원리

이것은 xnor 알고리즘의 정리 된 버전으로 이상한 경우를보다 간단하게 처리합니다 (편집 : xnor가 동일한 방식으로 정리 한 것처럼 보입니다).

[a, b, c, d, e, f, g] ↦
[a, bc, de, fg] ↦
[(bc) (de) (fg), a (de) (fg), a (bc) ( fg), a (bc) (de)] 재귀 ↦
[(bc) (de) (fg), a (de) (fg) c, a (de) (fg) b, a (bc) (fg) e, a (bc) (fg) d, a (bc) (de) g, a (bc) (de) f]

[a, b, c, d, e, f, g, h] ↦
[ab, cd, ef, gh] ↦
[(cd) (ef) (gh), (ab) (ef) (gh), ( 재귀에 의한 ab) (cd) (gh), (ab) (cd) (ef)]
([(cd) (ef) (gh) b, (cd) (ef) (gh) a, (ab) (ef ) (gh) d, (ab) (ef) (gh) c, (ab) (cd) (gh) f, (ab) (cd) (gh) e, (ab) (cd) (ef) h, (ab) (cd) (ef) g].


"배열에 n 개의 숫자를 제공합니다 (정수라고 가정 할 수 없음),"정수라고 가정 할 수 없습니다

5

O (n log n) 연산, 점수 = 9974

이진 트리와 함께 작동합니다.

파이썬

l = list(map(int, input().split()))
n = len(l)

p = [0] * n + l
for i in range(n - 1, 1, -1):
  p[i] = p[i + i] * p[i + i+1]

def mul(x, y):
  if y == None:
    return x
  return x * y

r = [None] * n + [[None]] * n
for i in range(n - 1, 0, -1):
  r[i] = [mul(p[i + i + 1], x) for x in r[i + i]] + [mul(p[i + i], x) for x in r[i + i + 1]]

u = r[1]
j = 1
while j <= n:
  j += j
print(u[n+n-j:] + u[:n+n-j])

여기에는 목록 추가 작업과 입력 값이 아닌 숫자에 대한 산술이 필요합니다. 그것이 중요한지 확실하지 않습니다. 이 mul함수는 기본 사례에 대해 n 개의 연산을 저장하여 1을 곱하여 낭비하지 않도록합니다. 어쨌든 이것은 O (n log n) 연산입니다. 정확한 공식은 입력 숫자에 대한 산술 연산 만 계산하는 경우 j = floor(log_2(n))다음 과 같습니다 j * (2^(j + 1) - n) + (j + 1) * (2 * n - 2^(j + 1)) - 2.

외부 제품을 계산하지 않는다는 아이디어로 하나의 작업을 저장해 준 @xnor에게 감사드립니다!

마지막 부분은 누락 된 용어의 순서로 제품을 출력하는 것입니다.


수식을 정확하게 계산하기가 어려운 경우 수식을 실행 n = 1000하고 코드의 산술 연산을 계산하십시오.
Arthur

나는 10975 작업을 계산 ...?
HyperNeutrino 2016 년

p[i] = p[i + i] * p[i + i+1]계산되지 않습니다
HyperNeutrino

이것은 n log2 n + n작업 에 관한 것입니다 (O (nlogn) btw
HyperNeutrino

@HyperNeutrino의 연산 p[i] = p[i + i] * p[i + i + 1]은 곱셈 최적화에 의해 저장되어야합니다. 그러나 나는 너무 많은 것을 세었을 것입니다.
Nore

3

O ((n-2) * n) = O (n 2 ) : 사소한 해결책

이것은 각 하위 집합을 함께 곱하는 간단한 솔루션입니다.

파이썬

def product(array): # Requires len(array) - 1 multiplication operations
    if not array: return 1
    result = array[0]
    for value in array[1:]:
        result *= value
    return result

def getSubsetProducts(array):
    products = []
    for index in range(len(array)): # calls product len(array) times, each time calling on an array of size len(array) - 1, which means len(array) - 2 multiplication operations called len(array) times
        products.append(product(array[:index] + array[index + 1:]))
    return products

여기에는 n목록 추가 작업 도 필요 합니다. 그것이 중요한지 확실하지 않습니다. 이것이 허용되지 않으면 product(array[:index] + array[index + 1:])로 대체 product(array[:index]) * product(array[index + 1:])하여 수식을로 바꿉니다 O((n-1)*n).


답변에 자신의 점수를 추가 할 수 있습니다. 이 경우 998 * 1000입니다.
Arthur

product기능 O(n)조작이 필요하지 않습니까? 배열의 각 요소마다 하나씩 (이것은 쉽게 변경 될 수 있음 O(n-1))
Roman Gräf

@ RomanGräf 맞습니다. O (n-1)로 바꾸겠습니다. 지적 해 주셔서 감사합니다.
HyperNeutrino 2016 년

이것은 원자 코드 골프 로 변경되었습니다 ...
Outgolfer Erik

@EriktheOutgolfer 지금 내 점수가 어떻게 되나요? 내가 멍청한 짓을하지 않는 한, 태그와 사양이 서로 모순되지 않습니까?
HyperNeutrino 2016 년

3

파이썬, 7540

3 중 합병 전략. 나는 아직 큰 병합으로 이것보다 더 잘 할 수 있다고 생각합니다. O (n log n)입니다.

편집 : 잘못된 카운트를 수정했습니다.

count = 0
def prod(a, b):
    if a == 1: return b
    if b == 1: return a
    global count
    count += 1
    return a * b

def tri_merge(subs1, subs2, subs3):
    total1, missing1 = subs1
    total2, missing2 = subs2
    total3, missing3 = subs3

    prod12 = prod(total1, total2)
    prod13 = prod(total1, total3)
    prod23 = prod(total2, total3)

    new_missing1 = [prod(m1, prod23) for m1 in missing1]
    new_missing2 = [prod(m2, prod13) for m2 in missing2]
    new_missing3 = [prod(m3, prod12) for m3 in missing3]

    return prod(prod12, total3), new_missing1 + new_missing2 + new_missing3

def tri_partition(nums):
    split_size = len(nums) // 3
    a = nums[:split_size]
    second_split_length = split_size + (len(nums) % 3 == 2)
    b = nums[split_size:split_size + second_split_length]
    c = nums[split_size + second_split_length:]
    return a, b, c

def missing_products(nums):
    if len(nums) == 1: return nums[0], [1]
    if len(nums) == 0: return 1, []
    subs = [missing_products(part) for part in tri_partition(nums)]
    return tri_merge(*subs)

def verify(nums, res):
    actual_product = 1
    for num in nums:
        actual_product *= num
    actual_missing = [actual_product // num for num in nums]
    return actual_missing == res[1] and actual_product == res[0]

nums = range(2, int(input()) + 2)
res = missing_products(nums)

print("Verified?", verify(nums, res))
if max(res[1]) <= 10**10: print(res[1])

print(len(nums), count)

관련 함수는 missing_products전체 제품과 누락 된 요소가있는 모든 제품에 제공되는입니다.


곱셈을 세었 tri_merge습니까? 또한 당신은 교체 할 수 있습니다 2 * split_size + ...tri_partitionsplit_size + split_size + ....
Roman Gräf 2016 년

@ RomanGräf 귀하의 제안에 따라 재구성했습니다.
isaacg 2016 년

1

dc, 2994 점

#!/usr/bin/dc -f

# How it works:
# The required products are
#
#   (b × c × d × e × ... × x × y × z)
# (a) × (c × d × e × ... × x × y × z)
# (a × b) × (d × e × ... × x × y × z)
# ...
# (a × b × c × d × e × ... × x) × (z)
# (a × b × c × d × e × ... × x × y)
#
# We calculate each parenthesised term by
# multiplying the one above (on the left) or below
# (on the right), for 2(n-2) calculations, followed
# by the n-2 non-parenthesised multiplications
# giving a total of 3(n-2) operations.

# Read input from stdin
?

# We will store input values into stack 'a' and
# accumulated product into stack 'b'.  Initialise
# stack b with the last value read.
sb

# Turnaround function at limit of recursion: print
# accumulated 'b' value (containing b..z above).
[Lbn[ ]nq]sG

# Recursive function - on the way in, we stack up
# 'a' values and multiply up the 'b' values.  On
# the way out, we multiply up the 'a' values and
# multiply each by the corresponding 'b' value.
[dSalb*Sb
z1=G
lFx
dLb*n[ ]n
La*]dsFx

# Do the last a*b multiplication
dLb*n[ ]n

# And we have one final 'a' value that doesn't have a
# corresponding 'b':
La*n

정수 비교 z1=(마지막 값에 도달하면 재귀를 종료하는)가 무료 라고 가정합니다 . foreach다른 언어 와 동일합니다 .

데모

for i in '2 3 5' '2 3 5 7' '0 2 3 5' '0 0 1 2 3 4'
do printf '%s => ' "$i"; ./127147.dc <<<"$i"; echo
done
2 3 5 => 15 10 6
2 3 5 7 => 105 70 42 30
0 2 3 5 => 30 0 0 0
0 0 1 2 3 4 => 0 0 0 0 0 0

크고 작은 입력을 가진 데모 :

./127147.dc <<<'.0000000000000000000542101086242752217003726400434970855712890625 1 18446744073709551616'
18446744073709551616 1.0000000000000000000000000000000000000000000000000000000000000000 .0000000000000000000542101086242752217003726400434970855712890625

1

C ++, 점수 : 5990, O ([2NlogN] / 3)

이 구현은 이진 트리 조회 테이블을 사용합니다. 첫 번째 구현은 O (NlogN) 이었지만 마지막 순간 최적화는 모든 배열 요소의 곱에서 한 쌍을 뺀 + 2 곱셈으로 하루를 절약했습니다. 나는 이것이 여전히 16 % 정도 더 최적화 될 수 있다고 생각합니다 ...

디버깅 트레이스를 다시 쓰는 것보다 삭제하는 것이 더 쉽기 때문에 디버깅 트레이스를 남겨 두었습니다. :)

[편집] 실제 복잡도는 100에 대해 O ([2NlogN] / 3)에서 측정됩니다. 실제로 작은 세트의 경우 O (NlogN)보다 약간 나쁘지만 배열이 커질수록 O ([NlogN] / 2)쪽으로 경향이 있습니다 1 백만 요소 집합에 대해 매우 큰 O (0.57.NlogN)

#include "stdafx.h"
#include <vector>
#include <iostream>
#include <random>
#include <cstdlib>

using DataType = long double;

using DataVector = std::vector<DataType>;

struct ProductTree
{
    std::vector<DataVector> tree_;
    size_t ops_{ 0 };

    ProductTree(const DataVector& v) : ProductTree(v.begin(), v.end()) {}
    ProductTree(DataVector::const_iterator first, DataVector::const_iterator last)
    {
        Build(first, last);
    }

    void Build(DataVector::const_iterator first, DataVector::const_iterator last)
    {
        tree_.emplace_back(DataVector(first, last));

        auto size = std::distance(first, last);
        for (auto n = size; n >= 2; n >>= 1)
        {
            first = tree_.back().begin();
            last = tree_.back().end();

            DataVector v;
            v.reserve(n);
            while (first != last) // steps in pairs
            {
                auto x = *(first++);
                if (first != last)
                {
                    ++ops_;
                    x *= *(first++); // could optimize this out,small gain
                }
                v.push_back(x);
            }
            tree_.emplace_back(v);
        }
    }

    // O(NlogN) implementation... 
    DataVector Prod()
    {
        DataVector result(tree_[0].size());
        for (size_t i = 0; i < tree_[0].size(); ++i)
        {
            auto depth = tree_.size() - 1;
            auto k = i >> depth;
            result[i] = ProductAtDepth(i, depth);
        }
        return result;
    }

    DataType ProductAtDepth(size_t index, size_t depth) 
    {
        if (depth == 0)
        {
            return ((index ^ 1) < tree_[depth].size())
                ? tree_[depth][index ^ 1]
                : 1;
        }
        auto k = (index >> depth) ^ 1;

        if ((k < tree_[depth].size()))
        {
            ++ops_;
            return tree_[depth][k] * ProductAtDepth(index, depth - 1);
        }
        return ProductAtDepth(index, depth - 1);
    }    

    // O([3NlogN]/2) implementation... 
    DataVector Prod2()
    {
        DataVector result(tree_[0].size());
        for (size_t i = 0; i < tree_[0].size(); ++i)    // steps in pairs
        {
            auto depth = tree_.size() - 1;
            auto k = i >> depth;
            auto x = ProductAtDepth2(i, depth);
            if (i + 1 < tree_[0].size())
            {
                ops_ += 2;
                result[i + 1] = tree_[0][i] * x;
                result[i] = tree_[0][i + 1] * x;
                ++i;
            }
            else
            {
                result[i] = x;
            }
        }
        return result;
    }

    DataType ProductAtDepth2(size_t index, size_t depth)
    {
        if (depth == 1)
        {
            index = (index >> 1) ^ 1;
            return (index < tree_[depth].size())
                ? tree_[depth][index]
                : 1;
        }
        auto k = (index >> depth) ^ 1;

        if ((k < tree_[depth].size()))
        {
            ++ops_;
            return tree_[depth][k] * ProductAtDepth2(index, depth - 1);
        }
        return ProductAtDepth2(index, depth - 1);
    }

};


int main()
{
    //srand(time());

    DataVector data;
    for (int i = 0; i < 1000; ++i)
    {
        auto x = rand() & 0x3;          // avoiding overflow and zero vaolues for testing
        data.push_back((x) ? x : 1);
    }

    //for (int i = 0; i < 6; ++i)
    //{
    //  data.push_back(i + 1);
    //}

    //std::cout << "data:[";
    //for (auto val : data)
    //{
    //  std::cout << val << ",";
    //}
    //std::cout << "]\n";

    ProductTree pt(data);
    DataVector result = pt.Prod2();

    //std::cout << "result:[";
    //for (auto val : result)
    //{
    //  std::cout << val << ",";
    //}
    //std::cout << "]\n";
    std::cout << "N = " << data.size() << " Operations :" << pt.ops_ << '\n';

    pt.ops_ = 0;
    result = pt.Prod();

    //std::cout << "result:[";
    //for (auto val : result)
    //{
    //  std::cout << val << ",";
    //}
    //std::cout << "]\n";

    std::cout << "N = " << data.size() << " Operations :" << pt.ops_ << '\n';

    return 0;
}

완전성을 위해 @nore의 알고리즘을 추가하고 있습니다. 정말 좋으며 가장 빠릅니다.

class ProductFlat
{
private:
    size_t ops_{ 0 };

    void InitTables(const DataVector& v, DataVector& left, DataVector& right)
    {
        if (v.size() < 2)
        {
            return;
        }

        left.resize(v.size() - 1);
        right.resize(v.size() - 1);

        auto l = left.begin();
        auto r = right.rbegin();
        auto ol = v.begin();
        auto or = v.rbegin();

        *l = *ol++;
        *r = *or++;
        if (ol == v.end())
        {
            return;
        }

        while (ol + 1 != v.end())
        {
            ops_ += 2;
            *l = *l++ * *ol++;
            *r = *r++ * *or++;
        }
    }

public:
    DataVector Prod(const DataVector& v)
    {
        if (v.size() < 2)
        {
            return v;
        }

        DataVector result, left, right;
        InitTables(v, left, right);

        auto l = left.begin();
        auto r = right.begin();
        result.push_back(*r++);
        while (r != right.end())
        {
            ++ops_;
            result.push_back(*l++ * *r++);
        }
        result.push_back(*l++);
        return result;
    }

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