Tüm alt kümelerin ürününü almak için en hızlı algoritma


23

nBir dizideki sayılar göz önüne alındığında (tamsayı olduklarını varsayamazsınız), boyuttaki tüm alt kümelerin ürününü hesaplamak istiyorum n-1.

Bunu, tüm sayıları bir araya getirip ardından sayılardan hiçbiri sıfır olmadığı sürece sırayla her birine bölerek yapabilirsiniz. Ancak bunu bölünmeden ne kadar çabuk yapabilirsiniz?

Bölünmeye izin vermiyorsanız, n-1 boyutundaki tüm alt kümelerin ürününü hesaplamak için gereken minimum aritmetik işlem sayısı (örneğin çarpma ve toplama) nedir?

Açıkça (n-1)*nçarpımlarla yapabilirsiniz.

Açıklığa kavuşturmak için, çıktı nfarklı ürünlerdir ve okuma ve yazmadan belleğe izin verilenler dışındaki tek işlem çarpma, toplama ve çıkarma işlemlerinden ibarettir.

Örnek

Giriş üç numarası varsa 2,3,5, o zaman çıkış üç sayı olduğu 15 = 3*5, 10 = 2*5ve 6 = 2*3.

Kazanma kriteri

Cevaplar, kodlarının kullanacağı aritmetik işlemlerin sayısı için kesin bir formül vermelidir n. Hayatı basitleştirmek için, n = 1000formülünü değerlendirerek puanını değerlendireceğim. Ne kadar düşük olursa o kadar iyi.

Kodunuz için kesin bir formül oluşturmak çok zorsa, kodu çalıştırabilir n = 1000ve koddaki aritmetik işlemleri sayabilirsiniz. Kesin bir formül ancak en iyisidir.

n=1000Kolay karşılaştırma için cevabınıza puanınızı eklemelisiniz .


4
1 ile çarpmayı ücretsiz olarak sayabilir miyiz? Aksi takdirde, bunu yapan özel bir çarpma işlevini tanımlardım.
xnor

3
Sayıları yeterli sayıda boşluk 0 rakamıyla bir araya getirerek paralel olarak bir sürü çarpma işlemi yapmak kurallara aykırı olur mu?
xnor

1
Gibi işlemler Do +üzerinde endeksleri saymak? Bu durumda, dizi indekslemesi de sayılır mı? (çünkü ekleme ve dereferencing için sözdizimsel şeker sonra).
nore

2
@nore OK Ben pes ediyorum :) Sadece bir şekilde girişi içeren aritmetik işlemleri sayın.
Arthur

1
Açıkçası bunu yapabilir (n-1)*nçarpma Yani (n-2)*nsağ,?
Luis Mendo,

Yanıtlar:


25

Python, 3 (n-2) işlem, skor = 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)

Dizilerin kümülatif ürünlerini sırasıyla soldan ve sağdan diziler leftve rightiçerir.

EDIT: 3 (n-2), sadece çarpma kullanıyorsak, n> = 2 için gereken en uygun işlem sayısı olduğunu kanıtlar.

İndüksiyonla bunu yapacağız; Yukarıdaki algoritmaya göre, n> = 2 için, 3 (n-2) için gerekli çarpma sayısına bağlı bir alt sınır olduğunu kanıtlamamız gerekir.

N = 2 için en az 0 = 3 (2-2) çarpmaya ihtiyacımız var, bu yüzden sonuç önemsiz.

N> 2 olsun ve n - 1 elemanlar için varsayalım, en az 3 (n-3) çarpıma ihtiyacımız var. K çarpımına sahip n element için bir çözüm düşünün. Şimdi, bu öğelerin sonuncusunu 1'miş gibi kaldırıyoruz ve tüm çarpımları doğrudan onunla basitleştiriyoruz. Aynı zamanda, diğer tüm öğelerin ürününe yol açan çarpımı da kaldırırız, çünkü bölünmeye izin verilmediğinden, diğer öğelerin n-2 ürününü elde etmek için hiçbir zaman ara değer olarak kullanılamadığından buna gerek yoktur. Bu, bizi 1 çarpma ve n - 1 elementler için bir çözüm olarak bırakır.

İndüksiyon hipotezi ile 1> 3 (n-3) değerine sahibiz.

Şimdi, kaç çarpımın kaldırıldığına bir bakalım. Bunlardan biri, sonuncusu hariç, tüm öğelerin ürününe liderlik ediyordu. Üstelik, son eleman doğrudan en az iki çarpımda kullanıldı: eğer sadece bir tanesinde kullanılmışsa, diğer elemanların bir ürününde oluşan bir ara sonuçla çarparken kullanılır; Diyelim ki, genellik kaybı olmadan, bu ara sonuç, üründeki ilk elemanı içeriyordu. Öyleyse, tüm öğelerin ürününü elde etmenin bir yolu yoktur, ancak ilk öğeyi içeren her ürün, son öğeyi içeren ya da ilk öğeyi içerdiği için.

Dolayısıyla talep edilen teoremi ispatlayan k> = l + 3> = 3 (n-2) değerine sahibiz.


8
Bu çıkıyor Haskell çok temiz : f l = zipWith (*) (scanl (*) 1 l) (scanr (*) 1 $ tail l).
xnor

Yorumlar uzun tartışmalar için değildir; bu konuşma sohbete taşındı .
Dennis,

12

Haskell , skor 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

Çevrimiçi deneyin!

Diyelim ki liste verilmiş [a,b,c,d,e,f,g,h]. Önce onu çiftler halinde gruplandırırız [[a,b],[c,d],[e,f],[g,h]]. Ardından, pairsalmak için ürünlerinin yarı boyutta listesinde yinelenir.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)]

Biz ilk elemanını alırsak (c*d)*(e*f)*(g*h)çarpın o tarafından ve bve asırasıyla, hepimiz ama ürününü almak ave tüm ama b. Bunu her çift için yapmak ve o çiftin özdeyiş sonucu ile yapmak, nihai sonucu almaktır. Tek uzunluktaki kasa, tek elemanın özyinelemeli aşamaya eşleştirilmemesi durumunda özel olarak ele alınır ve geri kalan elemanların ürünü, onsuz üründür.

Çarpmaların sayısı t(n)olduğu n/2, eşleştirme ürünü için t(n/2)yinelemeli bir çağrı için başka ntek tek elemanlara sahip ürünler için. Bu t(n) = 1.5 * n + t(n/2)garip verir n. Garip için daha kesin sayıları kullanma nile çarpılması görmezden 1baz vaka puanı verir için 2997için n=1000.


Bu çok güzel.
Arthur

Benim cevabımdaki gibi puanın 2995 olmasının ve 2994 olmasının sebebi, daha sonra kesilen iki davanın iktidarsızlığında tüm sayıların çarpımını hesaplamasıdır. Belki dikkatli bir şekilde ele almak products_but_one', doğru uzunlukta bir şey döndürerek bunu önleyebilir.
nore

@ Daha fazla buldum çünkü sayımda fazladan bir çarpma oldu, çünkü temel durumun 1çarpmak için ücretsiz olduğunu unuttum . Sanırım dolgu 1 şeyleri etkilemiyor, fakat algoritmamı kullanmamaları için temizledim.
xnor

Bu kod girişin tamsayı olduğunu varsayıyor mu?

@ Lembik O yapar, ancak yalnızca isteğe bağlı tür açıklamalarda. Hepsini değiştireceğim float.
xnor

9

Haskell , skor 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'

Çevrimiçi deneyin!

Bir birleştirme-fethetme stratejisi, birleştirme türünü çok anımsatan. Herhangi bir indeksleme yapmaz.

İşlev partition, bölümün karşıt taraflarına alternatif öğeler koyarak listeyi olabildiğince eşit yarıya böler. Eksik olan ürünlerin listesi ve toplam ürünün listesi (p,r)ile her yarıya ait sonuçları tekrar tekrar birleştiriyoruz .rp

Tam listenin çıktısı için, eksik elemanın yarısından birinde olması gerekir. Bu elemana sahip olan ürün, içinde bulunduğu yarı ürün için eksik olan bir üründür, diğer yarı ürün için çarpılan ürün ile çarpılır. Bu yüzden eksik olan her ürünü diğer yarının tamamı ile çarparak sonuçların bir listesini çıkardık map(*p1)r2 ++ map(*p2)r1). Bu uzunluğun nolduğu çarpımları alır n. p1*p2Gelecekte kullanım için 1 çarpma işlemine mal olan yeni bir tam ürün yapmamız gerekiyor .

Bu operasyonların sayısı için genel özyinelemeye verir t(n)ile nbile: t(n) = n + 1 + 2 * t(n/2). Garip olanı benzer, ancak alt listelerden biri1 daha büyük. Özyinelemeyi yaparak, n*(log_2(n) + 1)tek / çift ayrım bu kesin değeri etkilese de çarpımları elde ederiz . Değerleri en fazla t(3)tarafından çarpılarak etmeyerek geliştirilir 1bir varyantını tanımlayarak (%)ait (*)olduğunu kısayolları _*1veya 1*_durumlar.

Bu verir 9975 çarpımların=1000 . Haskell'in tembelliğinin dış katmandaki kullanılmayan genel ürünün hesaplanmadığı anlamına geldiğine inanıyorum 9974; Eğer yanılıyorsam, açıkça ihmal edebilirim.


Bir dakika önce beni zaman damgası ile yendin.
nore

Formülü tam olarak n = 1000hesaplamak zorsa, sadece çalıştırıp koddaki aritmetik işlemleri saymaktan çekinmeyin .
Arthur

Kodumuz temelde aynı olduğundan, çarpımın nasıl yapıldığını 9974ve 9975çarpılmadığını anlamıyorum n = 1000(genel ürünün dış katmanda hesaplanması durumunda). Bir kapsıyor muydu 1bunu test için kullanılan girişte?
nore

@nore Haklısın, ben tek başıma kaldım. Çarpma işlevi çağrıları sayısı için özyineleme yapmak için kod yazdım. Çağrıları doğrudan saymak daha güvenilir olur; bunu Haskell'de nasıl yapacağımı bilen var mı?
xnor

1
@xnor kullanabilirsiniz tracegelen Debug.Tracebir catch-all ile | trace "call!" False = undefinedbekçi, sanırım. Ancak bu unsafePerformIObaşlık altında kullanır , bu yüzden gerçekten bir gelişme değil.
Soham Chowdhury

6

Haskell , skor 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]

Çevrimiçi deneyin!

Nasıl çalışır

Bu, xnor algoritmasının temizlenmiş bir versiyonudur. garip durumla daha basit bir şekilde ilgilenen (düzenleme: xnor aynı şekilde temizlemişe benziyor):

[a, b, c, d, e, f, g] ↦
[a, bc, de, fg] ↦
[(bc) (de) (fg), a (de) (fg), a (bc) ( fg), a (bc) (de)] özyinelemeli ↦
[(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, h]
ab [ab, cd, ef, gh] ↦
[(cd) (ef) (gh), (ab) (ef) (gh), ( ab) (cd) (gh), (ab) (cd) (ef)] özyineleme ile ↦
[(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].


"Bir dizideki n sayıları verilir (tamsayı olduklarını

5

O (n log n) işlemleri, skor = 9974

İkili bir ağaçla çalışır.

piton

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])

Bu aynı zamanda liste ekleme işlemlerini ve giriş değerleri olmayan sayılar üzerinde bazı aritmetik işlemleri gerektirir; önemli olup olmadığından emin değilim. mulİşlevi vardır kurtarmaya n operasyonlar temel durum için, herhangi bir durumda 1 ile çarpılarak bunları israf kaçınmaktır, bu O (n log n) operasyonları olduğunu. Kesin formül, yalnızca giriş numaralarındaki aritmetik işlemlerin sayılması durumunda j = floor(log_2(n)): ile j * (2^(j + 1) - n) + (j + 1) * (2 * n - 2^(j + 1)) - 2.

Dış ürünü hesaplamama fikriyle bir işlemi kaydettiği için @xnor'a teşekkürler!

Son kısım, ürünlerin eksik dönem sırasına göre çıkarılmasıdır.


Formülü tam olarak n = 1000hesaplamak zorsa, sadece çalıştırıp koddaki aritmetik işlemleri saymaktan çekinmeyin .
Arthur

10975 operasyonu saydım.
HyperNeutrino

p[i] = p[i + i] * p[i + i+1]sayılmaz
HyperNeutrino

Bu n log2 n + n(O (nlogn) btw) işlemleridir
HyperNeutrino

@HyperNeutrino içindeki işlemler p[i] = p[i + i] * p[i + i + 1]çarpma optimizasyonu ile kaydedilmelidir. Bununla birlikte, bir tanesini çok fazla sayabilirdim.
nore

3

O ((n-2) * n) = 0 (n 2 ): Önemsiz Çözüm

Bu sadece alt grupların her birini çoğaltan önemsiz bir çözümdür:

piton

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

Bunun nliste ekleme işlemleri gerektirdiğini de unutmayın ; önemli olup olmadığından emin değilim. Buna izin değilse, o zaman product(array[:index] + array[index + 1:])için değiştirilebilir product(array[:index]) * product(array[index + 1:])formülü değiştirir, O((n-1)*n).


Cevabınıza kendi puanınızı ekleyebilirsiniz. Bu durumda 998 * 1000.
Arthur

productİşlev O(n)işlemlerine gerek yok mu ? dizideki her öğe için bir tane (bunun kolayca değiştirilebileceği düşünüldüğünde O(n-1))
Roman Gräf

@ RomanGräf Doğru. Bunu O (n-1) olarak değiştireceğim ama bunu belirttiğiniz için teşekkürler.
HyperNeutrino

Bu, atom-kod-golf olarak değiştirildi ...
Outgolfer Erik

@EriktheOutgolfer Bu puanımı şimdi ne yapıyor? Açıkça aptal olmadığım sürece, etiket ve teknik özellikler şimdi birbiriyle çelişmiyor mu?
HyperNeutrino

3

Python, 7540

Üçlü bir birleştirme stratejisi. Sanırım, daha büyük bir birleşme ile, bundan daha iyisini yapabilirim. O (n log n).

EDIT: Bir yanlış sayım düzeltildi.

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)

İlgili fonksiyon, missing_productsgenel ürüne ve hepsine eksik eleman verenlerin işlevidir .


çarpmalarını saydın tri_mergemı? Ayrıca 2 * split_size + ...girişi tri_partitionile değiştirebilirsiniz split_size + split_size + ....
Roman Gräf

@ RomanGräf Önerinize göre yeniden yapılandırdım.
isaacg

1

dc, skor 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

Tamsayı karşılaştırmasının z1=(son değere ulaştığımızda özyinelemeyi sonlandıran) ücretsiz olduğunu varsayıyorum . Bu, foreachdiğer dillerdeki beğenilere eşdeğerdir .

Gösteriler

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

Büyük ve küçük girişlere sahip bir demo:

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

1

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

Bu uygulama bir ikili ağaç arama tablosu kullanır. İlk uygulamam O (NlogN) idi, ama tüm dizi öğelerinin ürününü eksi olarak gören son dakika optimizasyonu, günü kurtardı, + 2 çarpma. Bunun hala biraz daha iyileştirilebileceğini düşünüyorum, belki% 16 daha ...

Bazı hata ayıklama izlerini bıraktım, çünkü onları silmek onları yeniden yazmaktan daha kolay :)

[Düzenle] gerçek karmaşıklık 100 için O ([2NlogN] / 3) 'de ölçülür. Aslında küçük kümeler için O (NlogN)' dan biraz daha kötüdür, ancak dizi büyüdükçe O ([NlogN] / 2) 'ye doğru meyillidir. a 1 milyon element kümesi için çok büyük 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'un algoritmasını, bütünlüğü için ekliyorum. Gerçekten çok hoş ve en hızlısı.

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_;
    }
};
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.