Найшвидший алгоритм для отримання продукту всіх підмножин


23

Враховуючи nчисла в масиві (ви не можете припустити, що вони є цілими числами), я хотів би обчислити добуток усіх підмножин розміру n-1.

Це можна зробити, помноживши всі числа разом, а потім діливши їх по черзі, доки жодне з чисел не дорівнює нулю. Однак, як швидко ви можете зробити це без поділу?

Якщо ви не дозволяєте ділити, яка мінімальна кількість арифметичних операцій (наприклад, множення та додавання), необхідних для обчислення добутку всіх підмножин розміром 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 Я поступаюся :) Просто порахуйте арифметичні операції, які певним чином включають введення.
Артур

1
Зрозуміло, ви можете це зробити у (n-1)*nмноженні. Ви маєте на увазі (n-2)*n, правда?
Луїс Мендо

Відповіді:


25

Python, 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)

Масиви leftі rightмістять сукупні продукти масиву відповідно зліва та справа відповідно.

EDIT: Доведення, що 3 (n-2) - оптимальна кількість операцій, необхідних для n> = 2, якщо ми використовуємо лише множення.

Ми зробимо це за допомогою індукції; за вищенаведеним алгоритмом ми просто мусимо довести, що для n> = 2, 3 (n-2) є нижньою межею кількості необхідних множень.

Для n = 2 нам потрібно принаймні 0 = 3 (2-2) множення, тому результат тривіальний.

Нехай n> 2, і припустимо, що для n - 1 елементів нам потрібно принаймні 3 (n-3) множення. Розглянемо рішення для n елементів з k множеннями. Тепер ми видаляємо останній з цих елементів так, ніби він був 1, і спрощуємо всі множення безпосередньо ним. Ми також видаляємо множення, що веде до добутку всіх інших елементів, оскільки це не потрібно, оскільки його ніколи не можна використовувати як проміжне значення для отримання добутку n-2 інших елементів, оскільки поділ не дозволено. Це залишає нам l множення і рішення для n - 1 елементів.

За індукційною гіпотезою маємо l> = 3 (n-3).

Тепер давайте подивимось, скільки множин було вилучено. Один з них був тим, що веде до твору всіх елементів, крім останнього. Більше того, останній елемент використовувався безпосередньо принаймні у двох множеннях: якщо він використовувався лише в одному, то він використовувався при множенні на проміжний результат, що складається з деякого добутку інших елементів; скажімо, не втрачаючи загальності, цей проміжний результат включав перший елемент у продукт. Тоді не можна отримати добуток усіх елементів, окрім першого, оскільки кожен продукт, який містить останній елемент, є або останнім елементом, або містить перший елемент.

Таким чином, маємо k> = l + 3> = 3 (n-2), що доводить заявлену теорему.


8
Це виходить дуже чисто в Haskell : f l = zipWith (*) (scanl (*) 1 l) (scanr (*) 1 $ tail l).
xnor

Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Денніс

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), і помножити його bі , aвідповідно, ми отримуємо продукт все , але aі все , крім b. Роблячи це для кожної пари та рекурсивного результату при відсутності цієї пари, ми отримуємо кінцевий результат. Корпус непарної довжини спеціально обробляється тим, що непарний елемент передається неспареним на рекурсивному етапі, а добуток повернених елементів - це продукт без нього.

Кількість множень t(n)- n/2для продукту, t(n/2)що поєднується , для рекурсивного виклику та іншого nдля продуктів з окремими елементами. Це дає t(n) = 1.5 * n + t(n/2)незвичайні n. Використання більш точних підрахунків для непарних nі ігнорування множення на 1для базового випадку дає бал 2997для n=1000.


Це дуже приємно.
Артур

Я думаю, що причина, чому оцінка дорівнює 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загального продукту.

Для виходу для повного списку відсутній елемент повинен бути в одній із половинок. Товар, у якому відсутній цей елемент, є продуктом, який не вистачає на половину, в якій він знаходиться, помноженому на повний продукт для другої половини. Отже, ми множимо кожен продукт з одним-відсутнім на повний продукт другої половини і робимо список результатів, як 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. Я вважаю, що лінь Хаскелла означає, що невикористаний загальний продукт у зовнішньому шарі не обчислюється 9974; якщо я помиляюся, я можу це явно пропустити.


Ти побив мене часовою міткою на хвилину раніше.
nore

Якщо формулу складно точно розробити, не соромтеся просто запустити її n = 1000і порахувати арифметичні операції в коді.
Артур

Оскільки наш код в основному однаковий, я не розумію, як ви потрапили до 9974чи не 9975множимо для n = 1000(у випадку обчислення загального продукту у зовнішньому шарі). Ви включили 1у вхід, який ви використовували для його тестування?
nore

@nore Ти маєш рацію, я був одній. Я написав код, щоб зробити рекурсію для кількості викликів функції множення. Підрахунок прямих дзвінків був би більш надійним - хтось знає, як я це зробив у Haskell?
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Функція є , щоб зберегти п операцій для базового випадку, щоб не витрачати їх шляхом множення на 1. У будь-якому випадку, це O (N журнал N) операцій. Точна формула є, якщо тільки підрахунок арифметичних операцій над числами вхідних з j = floor(log_2(n)): j * (2^(j + 1) - n) + (j + 1) * (2 * n - 2^(j + 1)) - 2.

Дякуємо @xnor за те, що зберегла одну операцію з ідеєю не обчислювати зовнішній продукт!

Остання частина полягає у виведенні продуктів у порядку відсутнього терміну.


Якщо формулу складно точно опрацювати, не соромтеся просто запустити її n = 1000і порахувати арифметичні операції в коді.
Артур

Я нарахував 10975 операцій ...?
HyperNeutrino

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 в цьому випадку.
Артур

Вам не потрібні ваші productфункціональні O(n)операції? по одному для кожного елемента в масиві (але, мабуть, це легко змінити O(n-1))
Роман Греф

@ RomanGräf True. Я зміню його на O (n-1), але дякую, що вказав на це.
HyperNeutrino

Це було змінено на атомно-код-гольф ...
Ерік Аутгольфер

@EriktheOutgolfer Що тепер робить моя оцінка? Якщо я не є явно дурним, чи не суперечать між собою тег та характеристики один одному?
HyperNeutrino

3

Пітон, 7540

Тристороння стратегія злиття. Я думаю, що можу зробити навіть краще, ніж це, з ще великим злиттям. Це O (n log n).

EDIT: виправлено помилку.

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_partitionс split_size + split_size + ....
Роман Греф

@ RomanGräf Я реструктуризував це відповідно до вашої пропозиції.
isaacg

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% ...

У мене залишилися сліди налагодження, лише тому, що їх простіше видалити, ніж переписати :)

[Редагувати] фактичну складність вимірюють при O ([2NlogN] / 3) на 100. Насправді це трохи гірше, ніж O (NlogN) для малих наборів, але має тенденцію до O ([NlogN] / 2) у міру зростання масиву дуже великий O (0,57.NlogN) для набору з 1 мільйона елементів.

#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_;
    }
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.