Чрезвычайно большой средневзвешенный

Я использую 64-битную матрицу с 32 г оперативной памяти (именно так вы знаете).

У меня есть файл (вектор) из 1,3 миллиона чисел (целые числа). Я хочу сделать другой вектор той же длины, где каждая точка представляет собой взвешенное среднее для всего первого вектора, взвешенное на обратном расстоянии от этой позиции (на самом деле это позиция ^ -0,1, а не ^ -1, но, например, цели), Я не могу использовать функцию matlab 'filter', потому что он может только усреднять вещи до текущей точки, правильно? Чтобы более четко объяснить, здесь приведен пример из трех элементов

data = [ 2 6 9 ]
weights = [ 1 1/2 1/3; 1/2 1 1/2; 1/3 1/2 1 ]
results=data*weights= [ 8 11.5 12.666 ]
i.e.
8 = 2*1 + 6*1/2 + 9*1/3
11.5 = 2*1/2 + 6*1 + 9*1/2
12.666 = 2*1/3 + 6*1/2 + 9*1

Таким образом, каждая точка нового вектора представляет собой взвешенное среднее для всего первого вектора, взвешенное на 1/(расстояние от этой позиции + 1).

Я мог бы просто переделать вектор веса для каждой точки, а затем вычислить элемент вектора результатов по элементу, но для этого требуется 1,3 миллиона итераций цикла for, каждый из которых содержит 1,3 миллиона умножений. Я бы предпочел использовать прямое матричное умножение, умножив 1x1.3mil на 1.3milx1.3mil, который работает в теории, но я не могу загрузить большую массив.

Затем я пытаюсь сделать матрицу с помощью оболочки script и индексировать ее в matlab, так что только соответствующий столбец матрицы вызывается одновременно, но это также занимает очень много времени.

Мне не нужно делать это в Matlab, поэтому любые советы, которые люди могут использовать, используют такие большие числа и получают средние оценки. Поскольку я использую вес ^ -0,1, а не ^ -1, он не падает так быстро - миллионная точка все еще взвешена на 0,25 по сравнению с исходными весами точек 1, поэтому я не могу просто ее разрезать так как он становится большим.

Надеюсь, это было достаточно ясно?

Вот код для ответа ниже (поэтому его можно отформатировать?):

data = load('/Users/mmanary/Documents/test/insertion.txt');
data=data.';
total=length(data);
x=1:total;
datapad=[zeros(1,total) data];
weights = ([(total+1):-1:2 1:total]).^(-.4);
weights = weights/sum(weights);
Fdata = fft(datapad);
Fweights = fft(weights);
Fresults = Fdata .* Fweights;
results = ifft(Fresults);
results = results(1:total);
plot(x,results)

Ответ 1

Единственный разумный способ сделать это - с сверткой FFT, как подкрепляет функцию filter и тому подобное. Это очень легко сделать вручную:

% Simulate some data
n = 10^6;
x = randi(10,1,n);
xpad = [zeros(1,n) x];

% Setup smoothing kernel
k = 1 ./ [(n+1):-1:2 1:n];

% FFT convolution
Fx = fft(xpad);
Fk = fft(k);

Fxk = Fx .* Fk;

xk = ifft(Fxk);
xk = xk(1:n);

Делает менее половины секунды для n=10^6!

Ответ 2

Это, вероятно, не самый лучший способ сделать это, но с большой памятью вы можете точно распараллелить процесс.

Вы можете построить разреженные матрицы, состоящие из записей исходной матрицы, которые имеют значение i^(-1) (где i = 1 .. 1.3 million), умножить их на исходный вектор и суммировать все результаты вместе.

Итак, для вашего примера продукт будет по существу:

a = rand(3,1);
b1 = [1 0 0;
      0 1 0;
      0 0 1];
b2 = [0 1 0;
      1 0 1;
      0 1 0] / 2;
b3 = [0 0 1;
      0 0 0;
      1 0 0] / 3;

c = sparse(b1) * a + sparse(b2) * a + sparse(b3) * a;

Конечно, вы бы не построили разреженные матрицы таким образом. Если вы хотите иметь меньше итераций внутреннего цикла, в каждой матрице может быть более одного из i.

Посмотрите на цикл parfor в MATLAB: http://www.mathworks.com/help/toolbox/distcomp/parfor.html

Ответ 3

Я не могу использовать функцию matlab 'filter', потому что он может только усреднять вещи до текущей точки, правильно?

Это неверно. Вы всегда можете добавлять образцы (например, добавлять или удалять нули) из ваших данных или из отфильтрованных данных. Поскольку фильтрация с помощью filter (кстати, вы также можете использовать conv), это линейное действие, оно не изменит результат (это как добавление и удаление нулей, которое ничего не делает, а затем фильтрация. Тогда линейность позволяет вам чтобы поменять порядок, чтобы добавить образцы → фильтр → удалить образец).

В любом случае, в вашем примере вы можете использовать ядро ​​усреднения:

weights = 1 ./ [3 2 1 2 3]; % this kernel introduces a delay of 2 samples

а затем просто:

result =  filter(w,1,[data, zeros(1,3)]); % or conv (data, w)
% removing the delay introduced by the kernel
result = result (3:end-1);

Ответ 4

Вы рассмотрели только 2 варианта: Умножение матрицы 1.3M * 1.3M с вектором один раз или умножение на 2 1.3M вектора в 1.3M раз.

Но вы можете разделить свою весовую матрицу на столько подматриц, сколько хотите, и умножить матрицу n * 1,3M с вектором 1,3M/n раз.

Я предполагаю, что самый быстрый будет, когда будет наименьшее количество итераций, и n таково, что создает самую большую подматрицу, которая помещается в вашу память, без того, чтобы ваш компьютер начал заменять страницы на ваш жесткий диск.

с размером памяти вы должны начать с n = 5000.

вы также можете сделать это быстрее, используя parfor (с n, деленным на количество процессоров).

Ответ 5

Скорее всего, это будет работать с вами, с одной незначительной оптимизацией в миксе.

Операции ^ -0.1 для создания весов потребуют намного больше времени, чем операции + и * для вычисления взвешенных средств, но вы повторно используете весы для всех миллионов взвешенных сред. Алгоритм становится следующим:

  • Создайте вектор весов со всеми весами, которые потребуются любому вычислению: weights = (-n:n).^-0.1

  • Для каждого элемента в векторе:

    • Индексируйте значимую часть вектора weights, чтобы рассмотреть текущий элемент как "центр".

    • Выполните взвешенное значение с частью веса и всем вектором. Это можно сделать с быстрым умножением на многоточие векторов, за которым следует скалярное деление.

Основной цикл делает n ^ 2 дополнений и долей. При n равном 1,3 миллионам это 3,4 триллиона операций. Одно ядро ​​современного 3GHz CPU может сделать 6 миллиардов дополнений/умножений в секунду, так что выйдет около 10 минут. Добавьте время для индексации вектора weights и накладных расходов, и я по-прежнему считаю, что вы можете прийти через полчаса.