Рассчитать сумму префикса
У меня есть следующий код для выполнения задачи сумма префикса:
#include <iostream>
#include<math.h>
using namespace std;
int Log(int n){
int count=1;
while (n!=0){
n>>=1;
count++;
}
return count;
}
int main(){
int x[16]={39,21,20,50,13,18,2,33,49,39,47,15,30,47,24,1};
int n=sizeof(x)/sizeof(int );
for (int i=0;i<=(Log(n)-1);i++){
for (int j=0;j<=n-1;j++){
if (j>=(std::powf(2,i))){
int t=powf(2,i);
x[j]=x[j]+x[j-t];
}
}
}
for (int i=0;i<n;i++)
cout<<x[i]<< " ";
return 0;
}
С этой страницы википедии, но я получил неправильный результат, что не так? пожалуйста помоги
3 ответа
Самый быстрый способ заставить ваш алгоритм работать: отбросить внешний for(i...)
петля, вместо установки i
до 0, и использовать только внутренний for (j...)
петля.
int main(){
...
int i=0;
for (int j=0;j<=n-1;j++){
if (j>=(powf(2,i))){
int t=powf(2,i);
x[j]=x[j]+x[j-t];
}
}
...
}
Или эквивалентно:
for (int j=0; j<=n-1; j++) {
if (j>=1)
x[j] = x[j] + x[j-1];
}
... который является интуитивно понятным способом суммирования префиксов, а также, вероятно, самым быстрым непараллельным алгоритмом.
Алгоритм Википедии разработан для параллельной работы, так что все дополнения полностью независимы друг от друга. Он считывает все значения, добавляет их, затем записывает их обратно в массив, все параллельно. В вашей версии, когда вы выполняете x[j]=x[j]+x[jt], вы используете x [jt], к которому вы только что добавили, t
итерации назад
Если вы действительно хотите воспроизвести алгоритм Википедии, вот один из способов, но имейте в виду, что он будет намного медленнее, чем описанный выше интуитивный способ, если только вы не используете распараллеливающий компилятор и компьютер с целой кучей процессоров.
int main() {
int x[16]={39,21,20,50,13,18,2,33,49,39,47,15,30,47,24,1};
int y[16];
int n=sizeof(x)/sizeof(int);
for (int i=0;i<=(Log(n)-1);i++){
for (int j=0;j<=n-1;j++){
y[j] = x[j];
if (j>=(powf(2,i))){
int t=powf(2,i);
y[j] += x[j-t];
}
}
for (int j=0;j<=n-1;j++){
x[j] = y[j];
}
}
for (int i=0;i<n;i++)
cout<<x[i]<< " ";
cout<<endl;
}
Примечание: вы можете использовать 1<<i
вместо powf(2,i)
, для скорости. И, как упоминал ergosys, ваш Log()
функция нуждается в работе; значения, которые он возвращает, слишком высоки, что не повлияет на результат частичной суммы в этом случае, но заставит это занять больше времени.
Я не уверен, что должен делать ваш код, но реализовать префиксную сумму на самом деле довольно легко. Например, это вычисляет (исключительную) сумму префикса диапазона итератора, используя произвольную операцию:
template <typename It, typename F, typename T>
inline void prescan(It front, It back, F op, T const& id) {
if (front == back) return;
typename iterator_traits<It>::value_type accu = *front;
*front++ = id;
for (; front != back; ++front) {
swap(*front, accu);
accu = op(accu, *front);
}
}
Это соответствует стилю интерфейса алгоритмов стандартной библиотеки C++.
Чтобы использовать его из своего кода, вы можете написать следующее:
prescan(x, x + n, std::plus<int>());
Вы пытаетесь реализовать параллельную сумму префикса? Это имеет смысл только тогда, когда вы фактически распараллеливаете свой код. В настоящее время ваш код выполняется последовательно и не получает ничего от более сложной логики "разделяй и властвуй", которую вы, похоже, используете.
Кроме того, в вашем коде действительно есть ошибки. Самый важный из них:
for(int i=0;i<=(Log(n)-1);i++)
Здесь вы повторяете до floor(log(n)) - 1
, Но псевдокод утверждает, что вам на самом деле нужно повторять до ceil(log(n)) - 1
,
Кроме того, учтите это:
for (int j=0;j<=n-1;j++)
Это не очень обычно. Обычно вы пишете такой код следующим образом:
for (int j = 0; j < n; ++j)
Обратите внимание, что я использовал <
вместо <=
и скорректировал границы от j - 1
в j
, То же самое относится и к внешнему циклу, поэтому вы получите:
for (int i = 0; i < std::log(n); ++i)
Наконец, вместо std::powf
, вы можете использовать тот факт, что pow(2, x)
такой же как 1 << x
(то есть используя тот факт, что операционная база 2 эффективно реализована как битовые операции). Это означает, что вы можете просто написать:
if (j >= 1 << i)
x[j] += x[j - (1 << i)];
Я думаю, что std:: частичный_сум делает то, что вы хотите http://www.sgi.com/tech/stl/partial_sum.html