Algoritmo più veloce per prendere il prodotto di tutti i sottoinsiemi


23

Dati i nnumeri in un array (non si può presumere che siano numeri interi), vorrei calcolare il prodotto di tutti i sottogruppi di dimensioni n-1.

Puoi farlo moltiplicando tutti i numeri insieme e poi dividendoli uno per volta, purché nessuno dei numeri sia zero. Tuttavia, quanto velocemente puoi farlo senza divisione?

Se non si consente la divisione, qual è il numero minimo di operazioni aritmetiche (ad esempio moltiplicazione e addizione) necessarie per calcolare il prodotto di tutti i sottogruppi di dimensioni n-1?

Chiaramente puoi farlo in (n-1)*nmoltiplicazioni.

Per chiarire, l'output è nprodotti diversi e le uniche operazioni consentite oltre alla lettura e alla scrittura in memoria sono la moltiplicazione, l'addizione e la sottrazione.

Esempio

Se l'ingresso ha tre numeri 2,3,5, allora l'uscita è tre numeri 15 = 3*5, 10 = 2*5e 6 = 2*3.

Criterio vincente

Le risposte dovrebbero fornire una formula esatta per il numero di operazioni aritmetiche che il loro codice utilizzerà in termini di n. Per semplificarti la vita, collegherò semplicemente la n = 1000tua formula per giudicare il suo punteggio. Più basso è il migliore.

Se è troppo difficile produrre una formula esatta per il tuo codice, puoi semplicemente eseguirlo n = 1000e contare le operazioni aritmetiche nel codice. Una formula esatta sarebbe comunque la migliore.

Dovresti aggiungere il tuo punteggio n=1000alla tua risposta per un facile confronto.


4
Possiamo contare moltiplicando per 1 come gratuito? Altrimenti definirei una funzione di moltiplicazione personalizzata che lo fa.
xnor

3
Sarebbe contro le regole fare un sacco di moltiplicazioni in parallelo concatenando numeri insieme a un numero sufficiente di spaziatori 0 cifre?
xnor

1
Le operazioni come +sugli indici contano? In questo caso, conta anche l'indicizzazione di array? (dal momento che è dopo tutto lo zucchero sintattico per l'aggiunta e la dereferenziazione).
nore

2
@nore OK cedo :) Basta contare le operazioni aritmetiche che coinvolgono l'input in qualche modo.
Arthur,

1
Chiaramente puoi farlo in (n-1)*nmoltiplicazioni Intendi (n-2)*n, vero?
Luis Mendo,

Risposte:


25

Python, 3 (n-2) operazioni, punteggio = 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)

Gli array lefte rightcontengono i prodotti cumulati dell'array da sinistra e da destra, rispettivamente.

EDIT: prova che 3 (n-2) è il numero ottimale di operazioni necessarie per n> = 2, se utilizziamo solo la moltiplicazione.

Lo faremo per induzione; dall'algoritmo sopra, dobbiamo solo dimostrare che per n> = 2, 3 (n-2) è un limite inferiore sul numero di moltiplicazioni necessarie.

Per n = 2, abbiamo bisogno di almeno 0 = 3 (2-2) moltiplicazioni, quindi il risultato è banale.

Sia n> 2, e supponiamo che per n - 1 elementi, siano necessarie almeno 3 (n-3) moltiplicazioni. Prendi in considerazione una soluzione per n elementi con k moltiplicazioni. Ora, rimuoviamo l'ultimo di questi elementi come se fosse 1, e semplifichiamo tutte le moltiplicazioni direttamente da esso. Rimuoviamo anche la moltiplicazione che porta al prodotto di tutti gli altri elementi, poiché quello non è necessario in quanto non può mai essere usato come valore intermedio per ottenere il prodotto di n-2 degli altri elementi, poiché la divisione non è consentita. Questo ci lascia con 1 moltiplicazioni e una soluzione per n - 1 elementi.

Per ipotesi di induzione, abbiamo l> = 3 (n-3).

Ora diamo un'occhiata a quante moltiplicazioni sono state rimosse. Uno di questi era quello che portava al prodotto di tutti gli elementi tranne l'ultimo. Inoltre, l'ultimo elemento è stato utilizzato direttamente in almeno due moltiplicazioni: se è stato utilizzato solo in uno, è stato utilizzato durante la moltiplicazione per un risultato intermedio costituito da un prodotto degli altri elementi; diciamo, senza perdita di generalità, questo risultato intermedio includeva il primo elemento nel prodotto. Quindi, non è possibile ottenere il prodotto di tutti gli elementi tranne il primo, poiché ogni prodotto che contiene l'ultimo elemento è l'ultimo o contiene il primo elemento.

Abbiamo quindi k> = l + 3> = 3 (n-2), a dimostrazione del teorema rivendicato.


8
Questo risulta molto pulito in Haskell : f l = zipWith (*) (scanl (*) 1 l) (scanr (*) 1 $ tail l).
xnor

I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Dennis,

12

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

Provalo online!

Di 'che ci viene data la lista [a,b,c,d,e,f,g,h]. Prima lo raggruppiamo in coppie [[a,b],[c,d],[e,f],[g,h]]. Quindi, ricerchiamo l'elenco a metà delle dimensioni pairsdei loro prodotti per otteneresubresults

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

Se prendiamo il primo elemento (c*d)*(e*f)*(g*h)e lo moltiplichiamo per be arispettivamente, otteniamo il prodotto di tutti, ma di atutti, tranne b. In questo modo per ogni coppia e risultato ricorsivo con quella coppia mancante, otteniamo il risultato finale. Il caso di lunghezza dispari viene gestito in modo speciale facendo in modo che l'elemento dispari non venga accoppiato alla fase ricorsiva e il prodotto degli elementi rimanenti restituiti è il prodotto senza di esso.

Il numero di moltiplicazioni t(n)è n/2per il prodotto di associazione, t(n/2)per la chiamata ricorsiva e un altro nper i prodotti con singoli elementi. Questo dà t(n) = 1.5 * n + t(n/2)per dispari n. L'uso di conteggi più precisi per la dispari ne l'ignorare la moltiplicazione con 1per il caso base dà un punteggio 2997per n=1000.


Questo è molto carino.
Arthur,

Penso che il motivo per cui il punteggio sia 2995 e non 2994 come nella mia risposta è che calcola anche il prodotto di tutti i numeri nella non-potenza di due casi, che viene successivamente troncato. Forse un'attenta gestioneproducts_but_one' potrebbe evitarlo restituendo qualcosa della lunghezza corretta.
nore

@nore ho scoperto di avere una moltiplicazione aggiuntiva nel mio conteggio perché ho dimenticato che il caso base ha un 1 che è libero di moltiplicare. Penso che il padding 1 non abbia influenzato le cose, ma ho ripulito il mio algoritmo per non usarle.
xnor

Questo codice presuppone che l'input sia intero?

@Lembik Lo fa, ma solo nelle annotazioni di tipo opzionali. Li cambierò tutti in float.
xnor

9

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

Provalo online!

Una strategia di divisione e conquista, che ricorda molto il tipo di unione. Non esegue alcuna indicizzazione.

La funzione partitiondivide l'elenco in metà il più possibile uguali inserendo elementi alternati sui lati opposti della partizione. Uniamo in modo ricorsivo i risultati (p,r)per ciascuna metà, con rl'elenco dei prodotti mancanti di uno e pil prodotto complessivo.

Per l'output dell'elenco completo, l'elemento mancante deve trovarsi in una delle metà. Il prodotto con quell'elemento mancante è un prodotto mancante per la metà in cui si trova, moltiplicato per il prodotto completo per l'altra metà. Quindi, moltiplichiamo ogni prodotto con uno mancante per il prodotto completo dell'altra metà e facciamo un elenco dei risultati, come map(*p1)r2 ++ map(*p2)r1). Questo richiede nmoltiplicazioni, dov'è nla lunghezza. Dobbiamo anche realizzare un nuovo prodotto completo p1*p2per uso futuro, con un costo di 1 moltiplicazione in più.

Ciò fornisce la ricorsione generale per il numero di operazioni t(n)conn ancora: t(n) = n + 1 + 2 * t(n/2). Quello dispari è simile, ma uno degli elenchi secondari è 1più grande. Effettuando la ricorsione, otteniamo n*(log_2(n) + 1)moltiplicazioni, sebbene la distinzione dispari / pari influisca su quel valore esatto. I valori fino a t(3)vengono migliorati non moltiplicandoli 1definendo una variante (%)dei (*)collegamenti _*1o dei 1*_casi.

Questo da 9975 moltiplicazioni per n=1000. Credo che la pigrizia di Haskell significhi che il prodotto complessivo non utilizzato nello strato esterno non viene calcolato 9974; se sbaglio, potrei ometterlo esplicitamente.


Mi hai battuto al timestamp un minuto prima.
nore

Se è difficile elaborare esattamente la formula, sentiti libero di eseguirla n = 1000e contare le operazioni aritmetiche nel codice.
Arthur,

Dal momento che il nostro codice è sostanzialmente lo stesso, non capisco come si è arrivati 9974e non 9975per le moltiplicazioni n = 1000(nel caso del calcolo del prodotto complessivo nel livello esterno). Hai incluso 1a nell'input che hai usato per testarlo?
nore

@nore Hai ragione, ero fuori da uno. Ho scritto il codice per eseguire la ricorsione per il numero di chiamate di funzioni di moltiplicazione. Il conteggio diretto delle chiamate sarebbe più affidabile: qualcuno sa come lo farei in Haskell?
xnor

1
@xnor è possibile utilizzare traceda Debug.Traceun catch-all | trace "call!" False = undefinedguardia, penso. Ma questo usa unsafePerformIOsotto il cofano, quindi non è davvero un grande miglioramento.
Soham Chowdhury,

6

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

Provalo online!

Come funziona

Questa è una versione pulita di ripulita dell'algoritmo xnor che si occupa del caso dispari in un modo più semplice (modifica: sembra che xnor lo abbia ripulito allo stesso modo):

[a, b, c, d, e, f, g] ↦
[a, bc, de, fg] ↦
[(bc) (de) (fg), a (de) (fg), a (bc) ( fg), a (bc) (de)] per ricorsione ↦
[(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)] per ricorsione ↦
[(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].


"Dati n numeri in un array (non si può presumere che siano numeri interi)," Non possiamo presumere che siano numeri interi

5

O (n log n) operazioni, punteggio = 9974

Funziona con un albero binario.

Pitone

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

Ciò richiede anche operazioni di aggiunta di elenchi e un po 'di aritmetica sui numeri che non sono i valori di input; non sono sicuro se questo conta. La mulfunzione è lì per salvare n operazioni per il caso base, per evitare di sprecarle moltiplicandole per 1. In ogni caso, si tratta di O (n log n) operazioni. La formula esatta è, se solo contando operazioni aritmetiche su numeri di ingresso, con j = floor(log_2(n)): j * (2^(j + 1) - n) + (j + 1) * (2 * n - 2^(j + 1)) - 2.

Grazie a @xnor per aver salvato un'operazione con l'idea di non calcolare il prodotto esterno!

L'ultima parte consiste nell'output dei prodotti nell'ordine del termine mancante.


Se è difficile elaborare esattamente la formula, sentiti libero di eseguirla n = 1000e contare le operazioni aritmetiche nel codice.
Arthur,

Ho contato 10975 operazioni ...?
HyperNeutrino,

p[i] = p[i + i] * p[i + i+1]non viene conteggiato
HyperNeutrino il

Si tratta di n log2 n + noperazioni (che è O (nlogn) tra l'altro
HyperNeutrino,

@HyperNeutrino in cui le operazioni p[i] = p[i + i] * p[i + i + 1]devono essere salvate dall'ottimizzazione della moltiplicazione. Tuttavia, avrei potuto contarne uno in eccesso.
nore

3

O ((n-2) * n) = O (n 2 ): soluzione triviale

Questa è solo la banale soluzione che moltiplica insieme ciascuno dei sottoinsiemi:

Pitone

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

Si noti che ciò richiede anche noperazioni di aggiunta dell'elenco; non sono sicuro se questo conta. Se ciò non è consentito, è product(array[:index] + array[index + 1:])possibile sostituirlo con product(array[:index]) * product(array[index + 1:]), che cambia la formula in O((n-1)*n).


Puoi aggiungere il tuo punteggio alla risposta. 998 * 1000 in questo caso.
Arthur,

Non ha bisogno di vostre productfunzioni O(n)operazioni? uno per ogni elemento dell'array (anche se può essere facilmente modificato in O(n-1))
Roman Gräf,

@ RomanGräf True. Lo cambierò in O (n-1) ma grazie per averlo sottolineato.
HyperNeutrino,

Questo è stato cambiato in atomic-code-golf ...
Erik the Outgolfer il

@EriktheOutgolfer Cosa rende il mio punteggio adesso? A meno che non sia palesemente stupido, il tag e le specifiche non si contraddicono ora?
HyperNeutrino,

3

Python, 7540

Una strategia di unione tripartita. Penso di poter fare anche meglio di così, con una fusione ancora grande. È O (n log n).

EDIT: corretto un errore.

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)

La funzione rilevante è missing_products, che fornisce il prodotto complessivo e tutti quelli con un elemento mancante.


hai contato le moltiplicazioni tri_merge? Inoltre è possibile sostituire l' 2 * split_size + ...in tri_partitioncon split_size + split_size + ....
Roman Gräf,

@ RomanGräf L'ho ristrutturato secondo il tuo suggerimento.
isaacg,

1

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

Suppongo che il confronto dei numeri interi z1=(che termina la ricorsione quando raggiungiamo l'ultimo valore) sia gratuito. Questo è equivalente a artisti del calibro diforeach in altre lingue.

dimostrazioni

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

Una demo con input grandi e piccoli:

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

1

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

Questa implementazione utilizza una tabella di ricerca ad albero binario. La mia prima implementazione è stata O (NlogN), ma un'ottimizzazione dell'ultimo minuto, che cerca il prodotto di tutti gli elementi dell'array meno una coppia, + 2 moltiplicazioni salvate il giorno. Penso che questo potrebbe ancora essere ottimizzato un po 'di più, forse un altro 16% ...

Ho lasciato alcune tracce di debug, solo perché è più facile eliminarle che riscriverle :)

[Modifica] la complessità effettiva viene misurata su O ([2NlogN] / 3) per 100. In realtà è un po 'peggio di O (NlogN) per piccoli set, ma tende verso O ([NlogN] / 2) man mano che l'array cresce O molto grande (0,57.NlogN) per un set di 1 milione di elementi.

#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;
}

Sto aggiungendo l'algoritmo di @ nore, per completezza. È davvero bello ed è il più veloce.

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_;
    }
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.