Created
November 1, 2012 13:36
-
-
Save sld/3993668 to your computer and use it in GitHub Desktop.
erlang university homework
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
% 25. Создать приложение с тремя потоками. Потоки работают с массивами одинаковой размерности. | |
% Массивы содержат положительные числа. | |
% Потоки производят одно действие над массивом, находят произведение и сумму элементов массива, | |
% затем проверяют условие равенства сумм трех потоков или равенства произведений трех потоков, | |
% если условие выполняется, потоки завершают работу. В противном случае, все описанные шаги повторяются. | |
% Поток может производить над массивом следующие действия; заменить один произвольный четный элемент | |
% любым нечетным числом или заменить один произвольный нечетный элемент любым четным числом. | |
% РГР по параллельному программированию | |
-module (task25). | |
-export ([replace/3, find_first_diff/2, array_minus/2, index_of/2, | |
replace_odd_even/2, find_most_freq_elem_ind/1, run/0, | |
product/1, print_array/2, random_array/1]). | |
odd(Num) -> | |
Num rem 2 == 0. | |
even( Num ) -> | |
not odd(Num). | |
oddeven_or_evenodd( E1, E2 ) -> | |
case odd(E1) and even(E2) of | |
true -> | |
true; | |
false -> | |
case even(E1) and odd(E2) of | |
true -> | |
true; | |
false -> | |
false | |
end | |
end. | |
arrays_equal(A, B) -> | |
(A--B)==[]. | |
check_sums_eq( A, B, C) -> | |
(A == B) and (B == C). | |
check_prods_eq( A, B, C) -> | |
(A == B) and (B == C). | |
% Проверка равнества суммы или произведения трех массивов | |
check_sums_or_products_eq( E1, E2 ,E3 ) -> | |
{From1, Sum1, Prod1} = E1, | |
{From2, Sum2, Prod2} = E2, | |
{From3, Sum3, Prod3} = E3, | |
check_sums_eq(Sum1, Sum2, Sum3) or check_prods_eq( Prod1, Prod2, Prod3 ). | |
print_array( List, Node ) -> | |
Printer = fun(E) -> io:format("[~w]E: ~p~n",[Node, E]) end, | |
lists:foreach(Printer,List). | |
random_array(N) -> randomize(N, []). | |
randomize(0, Array) -> Array; | |
randomize(N, Array) -> randomize(N-1, [random:uniform(300) | Array]). | |
product( [H | T] ) -> | |
product( T, H*1 ). | |
product( [], Prod) -> | |
Prod; | |
product( [H | T], Prod) -> | |
product( T, H*Prod). | |
index_of(Item, List) -> index_of(Item, List, 0). | |
index_of(_, [], _) -> not_found; | |
index_of(Item, [Item|_], Index) -> Index; | |
index_of(Item, [_|Tl], Index) -> index_of(Item, Tl, Index+1). | |
array_minus( [H1 | Arr1], [H2 | Arr2] ) -> | |
array_minus( Arr1, Arr2, [H1 - H2] ). | |
array_minus( [], [], MinusArr ) -> | |
lists:reverse( MinusArr ); | |
array_minus( [H1 | Arr1], [H2 | Arr2], MinusArr ) -> | |
Minus = H1 - H2, | |
array_minus( Arr1, Arr2, [Minus | MinusArr] ). | |
%replace([1,2,3], 0, 33) => [33,2,3] | |
replace( [H | T], Ind, NewEl ) -> | |
replace( T, [H], 0, Ind, NewEl ). | |
replace( Tarr, [H | Harr], Ind, Ind, NewEl) -> | |
lists:reverse(Harr) ++ [ NewEl | Tarr ]; | |
replace( [H | T], Harr, CntInd, Ind, NewEl ) -> | |
replace( T, [H | Harr], CntInd + 1, Ind, NewEl ). | |
find_not_zero( Arr ) -> | |
find_not_zero( Arr, 0 ). | |
find_not_zero( [], Ind ) -> | |
{ not_found, 0 }; | |
find_not_zero( [H | T], Ind ) -> | |
if | |
not(H == 0) -> | |
Ind; | |
true -> | |
find_not_zero( T, Ind+1) | |
end. | |
diff_elem_not_found( Status1, Status2 ) -> | |
case Status1 of | |
not_found -> | |
case Status2 of | |
not_found -> | |
{not_found, couple}; | |
found -> | |
{not_found, first} | |
end; | |
found -> | |
case Status2 of | |
not_found -> | |
{not_found, second}; | |
found -> | |
false | |
end | |
end. | |
% Находим index most frequent value( наиболее часто встречающееся значение ) в массиве | |
% [1,2,2,3] => 2 | |
% Результат возвращается для отсортированного массива | |
% Так что лучше возвращать само значение, а не индекс | |
find_most_freq_elem_ind_sort( NotFoundArr ) -> | |
[H | SortedList] = lists:sort( NotFoundArr ), | |
find_most_freq_elem_ind_sort( SortedList, {H , 1}, 1, 0, 1 ). | |
find_most_freq_elem_ind_sort( [], {H , Cnt}, MaxCnt, MaxInd, CurInd ) -> | |
case Cnt > MaxCnt of | |
true -> | |
CurInd; | |
false -> | |
MaxInd | |
end; | |
find_most_freq_elem_ind_sort( [H | T], {H , Cnt}, MaxCnt, MaxInd, CurInd ) -> | |
case Cnt+1 > MaxCnt of | |
true -> | |
find_most_freq_elem_ind_sort( T, {H , Cnt+1}, Cnt+1, CurInd, CurInd+1 ); | |
false -> | |
find_most_freq_elem_ind_sort( T, {H , Cnt+1}, MaxCnt, MaxInd, CurInd+1 ) | |
end; | |
find_most_freq_elem_ind_sort( [H | T], {El , Cnt}, MaxCnt, MaxInd, CurInd ) -> | |
find_most_freq_elem_ind_sort( T, {H , 1}, MaxCnt, MaxInd, CurInd+1 ). | |
% Определяем MFV для исходного массива, путем поиска в отсортированном | |
find_most_freq_elem_ind( NotFoundArr ) -> | |
SortedInd = find_most_freq_elem_ind_sort( NotFoundArr ), | |
SortedList = lists:sort( NotFoundArr ), | |
El = lists:nth( SortedInd+1, SortedList ), | |
index_of( El, NotFoundArr ). | |
% Находим most common value для массива без уникального элемента сравниваем с массивом содержащий | |
% уникальный элемент -> [1,2,3,2] AND [1,2,3,4] => {filtered, 1} | |
filter_not_found_diff_elem( NotFoundArr, Ind, Arr ) -> | |
NewInd = find_most_freq_elem_ind( NotFoundArr ), | |
{ filtered, NewInd }. | |
% Находим индекс элемента в массиве Arr1, который отличается от всех в Arr2 | |
% [1,2,3,4] AND [2,3,4,5] => {found, 0} | |
find_diff_elem_ind( Arr1, Arr2 ) -> | |
find_diff_elem_ind( Arr1, Arr2, 0). | |
find_diff_elem_ind( [], Arr2, Cnt) -> | |
{not_found, diff_elem}; | |
find_diff_elem_ind( [ H | T ], Arr2, Cnt) -> | |
case lists:member(H, Arr2) of | |
false -> | |
{found, Cnt}; | |
true -> | |
find_diff_elem_ind( T, Arr2, Cnt+1 ) | |
end. | |
% Возвращает позиции элементов которых нету в обоих массивах одновременно | |
%find_first_diff([1,2,3], [2,1,4]) -> 2, 2 | |
%find_first_diff([1,2,3,20,30], [2,1,4,30,40]) -> 2, 2 | |
%find_first_diff([1,2,20,30], [2,1,30,40]) -> 2, 3 | |
% Если в одном массиве есть уникальный элемент, а во втором нету | |
% например [1,2,2,3] AND [1,2,3,4] - то вернется индекс наиболее часто встречающегося | |
% элемента в первом и уникального во втором массиве -> {diff, 2, 3} | |
% Если в обоих нету, то вернутся два случайных индекса -> [1,2,2,3] AND [1,1,2,3] -> {diff, 0, 3} | |
find_first_diff( Arr1, Arr2 ) -> | |
{Status1, Ind1} = find_diff_elem_ind( Arr1, Arr2 ), | |
{Status2, Ind2} = find_diff_elem_ind( Arr2, Arr1 ), | |
case diff_elem_not_found( Status1, Status2 ) of | |
{not_found, couple} -> | |
NewInd1 = random:uniform( lists:flatlength(Arr1) - 1 ), %find_most_freq_elem_ind(Arr1), | |
NewInd2 = random:uniform( lists:flatlength(Arr2) - 1 ), %find_most_freq_elem_ind(Arr2), | |
{diff, NewInd1, NewInd2}; | |
{not_found, first} -> | |
{_, NewInd } = filter_not_found_diff_elem( Arr1, Ind2, Arr2 ), | |
{ diff, NewInd, Ind2 }; | |
{not_found, second} -> | |
{_, NewInd } = filter_not_found_diff_elem( Arr2, Ind1, Arr1 ), | |
{ diff, Ind1, NewInd }; | |
false -> | |
{ diff, Ind1, Ind2 } | |
end. | |
% Заменяем четный элемент на нечетный или наоборот | |
% Если в обоих массивах оба элемента четные или нечетные, то заменяем | |
% соответственно на нечетный(1) и четный(2) | |
% возвращает {replaced, RepArr1, RepArr2} | |
% [10,10,10] AND [21,21,21] -> [10,10,10] AND [10,21,21] | |
% [10,10,10] AND [20,20,20] -> [1,10,10] AND [1,20,20] | |
replace_odd_even( Arr1, Arr2 ) -> | |
{_, Arr1Ind, Arr2Ind } = find_first_diff( Arr1, Arr2 ), | |
Arr1Val = lists:nth( Arr1Ind+1, Arr1 ), | |
Arr2Val = lists:nth( Arr2Ind+1, Arr2 ), | |
case oddeven_or_evenodd( Arr1Val, Arr2Val ) of | |
true -> | |
RepArr2 = replace( Arr2, Arr2Ind, Arr1Val ), | |
{replaced, [], RepArr2}; | |
false -> | |
case odd( Arr1Val ) of | |
true -> | |
RepArr2 = replace( Arr2, Arr2Ind, 1 ), | |
RepArr1 = replace( Arr1, Arr1Ind, 1 ), | |
{replaced, RepArr1, RepArr2}; | |
false -> | |
RepArr2 = replace( Arr2, Arr2Ind, 2 ), | |
RepArr1 = replace( Arr1, Arr1Ind, 2 ), | |
{replaced, RepArr1, RepArr2} | |
end | |
end. | |
%% | |
%% Начинаем работу с процессами | |
%% | |
% Имитация критической секции - принадлежит процессу sum_prod_pid | |
wait_for_processing_arrays( [] ) -> | |
ok; | |
wait_for_processing_arrays( Arr ) -> | |
receive | |
{ processed, Pid } -> | |
wait_for_processing_arrays(lists:delete(Pid, Arr)) | |
end. | |
% Проверяем равенство произведения или суммы для потоков данных по условию задачи | |
% Если равны то завершаем пересчет массивов | |
% Иначе отправляем сообщение для пересчета массивов | |
% (From1 и From2) и (From1 и From3) | |
% Как только они пересчитаны( заменены четные на нечетные и тд ) отправляем сообщение | |
% процессу recalculator для пересчета сумм. | |
sum_prod_check3( [E1, E2, E3] ) -> | |
{F1, _, _} = E1, | |
{F2, _, _} = E2, | |
{F3, _, _} = E3, | |
% [From1, From2, From3] = lists:sort([F1, F2, F3]), | |
[From1, From2, From3] = [F1, F2, F3], | |
case check_sums_or_products_eq( E1, E2, E3 ) of | |
true -> | |
From1 ! finish, | |
From2 ! finish, | |
From3 ! finish, | |
recalculator ! stop; | |
false -> | |
From1 ! {send_array_to_process_in, From2}, | |
From1 ! {send_array_to_process_in, From3}, | |
wait_for_processing_arrays( [ From1, From2, From3] ), | |
recalculator ! recalculate | |
end. | |
% Проверка равенства произведения или суммы - начинается когда накапливается три массива | |
% на процессе проверки | |
sum_prod_check( Arr ) -> | |
receive | |
{ sum_prod, From, Sum, Prod } -> | |
NewArr = [{From, Sum, Prod} | Arr], | |
case (lists:flatlength(NewArr) == 3) of | |
true -> | |
sum_prod_check3( NewArr ), | |
sum_prod_check( [] ); | |
false -> | |
case (lists:flatlength(NewArr) > 3) of | |
true -> | |
sum_prod_check( [lists:last(NewArr)] ); | |
false -> | |
sum_prod_check( NewArr ) | |
end | |
end | |
end. | |
log( Pid, Arr ) -> | |
% ok. | |
file:write_file("./log", io_lib:fwrite("~w ~p.\n", [Pid, Arr]), [append]). | |
log( Pid, Arr, From, RepArr) -> | |
% ok. | |
file:write_file("./log", io_lib:fwrite("~w --> ~w ~p -- ~p.\n\n", [Pid, From, Arr, RepArr]), [append]). | |
% Основной процесс работы с массивом | |
process_array( Arr ) -> | |
receive | |
finish -> | |
io:format( "Finish Node:~w ~n", [self()]), | |
log( self(), Arr ), | |
process_array(Arr); | |
{ sum_prod, SumProdPid } -> | |
Sum = lists:sum( Arr ), | |
Prod = product( Arr ), | |
SumProdPid ! { sum_prod, self(), Sum, Prod}, | |
process_array( Arr ); | |
print_array -> | |
print_array(Arr, self()); | |
{ send_array_to_process_in, In } -> | |
In ! {process, self(), Arr}, | |
process_array( Arr ); | |
{ replace, From, RepArr } -> | |
log(self(), Arr, From, RepArr), | |
sum_prod_pid ! {processed, self()}, | |
process_array( RepArr ); | |
{ process, From, GetArray } -> | |
case arrays_equal(GetArray, Arr) of | |
true -> | |
sum_prod_pid ! {processed, self()}, | |
sum_prod_pid ! {processed, From}, | |
process_array( Arr ); | |
false -> | |
{_, Arr1, Arr2} = replace_odd_even( GetArray, Arr ), | |
log(self(), Arr, From, Arr2), | |
case not(Arr1 == []) of | |
true -> | |
From ! {replace, self(), Arr1}, | |
sum_prod_pid ! {processed, self()}, | |
process_array( Arr2 ); | |
false -> | |
sum_prod_pid ! {processed, self()}, | |
sum_prod_pid ! {processed, From}, | |
process_array( Arr2 ) | |
end | |
end | |
end. | |
% Функция посылает сообщение на process_array для проверки сумм и произведений в массивах | |
sumprod_recalculator() -> | |
receive | |
recalculate -> | |
pid1 ! {sum_prod, sum_prod_pid}, | |
pid2 ! {sum_prod, sum_prod_pid}, | |
pid3 ! {sum_prod, sum_prod_pid}, | |
sumprod_recalculator(); | |
stop -> | |
ok | |
end. | |
% Основная функция которая запускает процессы | |
% Результат записывается в файл log, местоположение которого совпадает с папкой приложения | |
run() -> | |
file:write_file("./log", ""), | |
% Size = 500, | |
% Arr1 = random_array(Size), | |
% Arr2 = random_array(Size), | |
% Arr3 = random_array(Size), | |
% Arr1 = [15,21,28,18,10,16,29,22,14,3], | |
% Arr2 = [1,13,14,7,17,5,21,7,5,18], | |
% Arr3 = [7,6,10,30,18,2,10,13,15,17], | |
Arr1 = [20,20,20], | |
Arr2 = [21,21,21], | |
Arr3 = [32,33,31], | |
% Arr1 = [2,1,1,25,8,10,25,26,27,2], | |
% Arr2 = [18,24,27,27,21,12,19,17,30,2], | |
% Arr3 = [3,20,21,18,12,3,24,13,22,9], | |
register(sum_prod_pid, spawn( fun() -> sum_prod_check( [] ) end )), | |
register(recalculator, spawn( fun() -> sumprod_recalculator() end ) ), | |
register(pid1, spawn( fun() -> process_array( Arr1 ) end ) ), | |
register(pid2, spawn( fun() -> process_array( Arr2 ) end) ), | |
register(pid3, spawn( fun() -> process_array( Arr3 ) end) ), | |
file:write_file("./log", io_lib:fwrite("~w ~p.\n ~w ~p.\n ~w ~p.\n", [whereis(pid1), Arr1, whereis(pid2), Arr2, whereis(pid3), Arr3]), [append]), | |
recalculator ! recalculate. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment