Сложение элементов двух массивов
у вас есть два массива
int[] a = {......} // Total elements 1 million
int[] b = {......} // Total elements 1 million , Length is same for both arrays.
Q1. Я должен создать массив
int[] c
элементы которого являются суммой соответствующих индексов A[] и b [] Бывший.
c[0] = a[0] + b[0];
c[1] = a[1] + b[1];
c[2] = a[2] + b[2];
решение: - я могу воспользоваться преимуществами многопоточности. Разделите весь массив на 10 или более частей и назначьте каждый сегмент потоку для выполнения вычисления. Примечание - > интервьюер предложил использовать многопоточность
Q2. Теперь его немного изменили.Элементы массива с имейте такие значения: ->
c[0] = a[0] + b[0];
c[1] = a[1] + b[1] + c[0]; // At this line c[0] is Sum of a[0] + b[0]; The Above Line
c[2] = a[2] + b[2] + c[1]; // At this line c[0] is Sum of a[1] + b[1]+ a[0] + b[0]; The Above Line
MySolution - > решить часть 1 (Q1) и создать временный массив, и после этого мы должны выполнить сложение, как это.
C[1] = temp[1]+temp[0]
C[2] = temp[2]+temp[1]
Примечание: - > нам действительно не нужен temp[], мы можем сделать это только с помощью массива c также. Просто объяснить это на так легко.
5 ответов
вы можете сделать часть 1 многопоточный, как вы говорите, давая -
temp[0] = a[0] + b[0];
temp[1] = a[1] + b[1];
temp[2] = a[2] + b[2];
etc....
тогда расчет для части 2 становится -
c[0] = temp[0];
c[1] = temp[1] + temp[0];
c[2] = temp[2] + temp[1] + temp[0];
c[3] = temp[3] + temp[2] + temp[1] + temp[0];
etc...
хотя это выглядит последовательным и невозможно распараллелить, на самом деле это довольно распространенная операция, называемая "суммой префикса" или "сканированием". Дополнительные сведения, в том числе о распараллеливании, см. В разделе Википедия или Blelloch.
в случае 8 элементов это становится следующим, где каждая рекурсивная фаза может быть распараллелен, поскольку каждый расчет не зависит от других вычислений на том же этапе.
// 1st phase
u[0] = temp[0] + temp[1];
u[1] = temp[2] + temp[3];
u[2] = temp[4] + temp[5];
u[3] = temp[6] + temp[7];
// 2nd phase
v[0] = u[0] + u[1];
v[1] = u[2] + u[3];
// 3rd phase
w[0] = v[0] + v[1];
// final phase
c[0] = temp[0];
c[1] = u[0];
c[2] = u[0] + temp[2];
c[3] = v[0];
c[4] = v[0] + temp[4];
c[5] = v[0] + u[2];
c[6] = v[0] + u[2] + temp[6];
c[7] = w[0];
на мой взгляд, для второго вопроса у вас есть две техники:
сначала нужно сделать два шага. Шаг 1 с помощью потоков вы можете добавить
c[0] = a[0] + b[0];
c[1] = a[1] + b[1];
c[2] = a[2] + b[2];
как вы предложили.
но Шаг-2 должен выполняться последовательно. потому что c[ i + 1]
значение зависит от обновленного значения c [i]
вторая техника немного сложна, но может быть быстрой.
то, что вас просят сделать во втором вопросе, - это сделать что-то вроде:
c[0] = a[0] + b[0];
c[1] = a[1] + b[1] + a[0] + b[0];
c[2] = a[2] + b[2] + a[1] + b[1] + a[0] + b[0];
это может быть параллельно.
c[i] = thread1( sum(a[0]...a[i] )) + thread2( sum(b[0]...b[i] ))
for i >= 0
хорошо в этой технике, вы можете рассчитать c[i]
параллельно для всех i
(два уровня резьбовые модели).
вы можете дополнительно улучшить функции thread1 / thread2 как многопоточные с дочерними потоками для выполнения sum – но помните, что когда-то многопоточный код работает медленно, а затем однопоточный код из-за времени переключения контекста потока (поэтому вы должны дать достаточное количество задач каждому потоку).
точка, которая не похожа на вторую технику, - это " большая часть того, что потоки для c[i]
сделать то же самое, что темы для c[i-1]
сделать так же".
Благодаря @jogojapan чтобы сообщить мне об этом недостатке.
для улучшения техники чтения обновляется!--33-->ответ г-н Jogojapan.
На самом деле вы можете использовать многопоточность в этой задаче. Ваш алгоритм будет состоять из двух частей:
применить алгоритм Q1 - эта часть будет использовать преимущество многопоточности.
просто в одном цикле примените formyla:
c[n] = c[n] + c[n-1]
,n=1...999999
.
вы можете использовать многопоточность здесь, на пути от 1-го вопроса. Я имею в виду, вы можете рассчитать
sumA0B0 = a[0] + b[0];
в отдельных потоках и даже ждать расчета (синхронизация ie на[i]). Затем в отдельном потоке вы можете вычислить c[i] = sumAiBi + c[i-1];
один из способов использования многопоточности для Q2-сделать это за два прохода (каждый с использованием T потоков внутри, где T можно выбрать свободно):
вычислить
c[i] = a[i] + b[i] + c[i-1]
для всех ячеек обычным многопоточным способом, т. е. после разделения входных данных на диапазоны [0, r1), [r1, r2),... [rk, n) и применение одного потока к каждому диапазону. Да, это будет неверно для всех диапазонов, кроме первого, и Шаг 2 исправит его.вычислить исправления, опять же многопоточным способом. Для этого мы ищем самое правильное значение каждого диапазона, т. е.
corr1:=c[r1-1]
,corr2:=corr1+c[r2-1]
,corr3:=corr2+c[r3-1]
etc., что дает нам исправления стоимостью для каждого потока, а затем вычислить, снова используя многопоточность с теми же диапазонами, что и раньше,c[i] += corrk
здесьcorrk
- это корректирующее значение для конкретного потока для k-го потока. (Для нулевого потока мы можем использоватьcorr0:=0
, так что поток не нужно делать что угодно.)
это улучшает теоретическое время выполнения фактором T, где T-количество потоков (которые могут быть выбраны свободно), поэтому это оптимальное решение в отношении многопоточности.
чтобы проиллюстрировать, как это работает, вот пример, где мы предполагаем массивов длины n==30
. Далее мы предполагаем, что используем 3 потока: один для вычисления диапазона c[0..9]
, для c[10..19]
и один для c[20..29]
.
ясно, что цель состоит в том, что в ячейке c[i]
, для любого 0<i<n
, мы получим
c[i] == a[0]+...+a[i]+b[0]+...+b[i]
(т. е. сумма всех a[0..i]
и b[0..i]
) после завершения алгоритма. Давайте посмотрим, как алгоритм туда попадает, например cell i==23
. Эта ячейка обрабатывается третьим потоком, т. е. потоком, ответственным за диапазон c[20..29]
.
Шаг 1: наборы потоков
c[20] = a[20]+b[20]
c[21] = c[20]+a[21]+b[21] == a[20]+a[21]+b[20]+b[21]
c[22] = c[21]+a[22]+b[22] == a[20]+a[21]+a[22]+b[20]+b[21]+b[22]
c[23] = c[22]+a[23]+b[23] == a[20]+a[21]+a[22]+a[23]+b[20]+b[21]+b[22]+b[23]
...
так, после того как раздел 1 заканчивал, мы у некоторых из a[20..23]
и b[20..23]
в ячейке c[23]
. Чего не хватает, так это суммы a[0..19]
и b[0..19]
.
аналогично, первый и второй потоки установили значения в c[0..9]
и c[10..19]
такое, что
c[0] = a[0]+b[0]
c[1] = c[0]+a[1]+b[1] == a[0]+a[1]+b[0]+b[1]
...
c[9] = a[0]+...+a[9]+b[0]+...+b[9]
и
c[10] = a[10]+b[10]
...
c[19] = a[10]+...+a[19]+b[10]+...+b[19]
Шаг 2: корректирующее значение для третьего потока,corr2
- сумма corr1
и справа значение, вычисленное вторым потоком, в то время как corr1
является самым правильным значением, вычисленным первым нитка. Отсюда
corr2 == c[9]+c[19] == (a[0]+...+a[9]+b[0]+...+b[9]) + (a[10]+...+a[19]+b[10]+...+b[19])
и это действительно сумма, отсутствующая в значении c[23]
после шага 1. На Шаге 2, мы добавляем это значение ко всем элементам c[20..29]
, следовательно, после завершения шага 2,c[23]
правильно (как и все другие клетки).
причина, по которой это работает, заключается в том, что вычисление значений ячеек является строго инкрементной операцией слева направо, а порядок операций для вычисления одной ячейки не имеет значения (потому что +
является ассоциативным и коммутативным). Следовательно, конечный результат ("самое правое значение") любого данного потока после шага 1 может быть использован для исправления результатов потоков, ответственных за диапазоны справа от него на Шаге 2.