IPB
ЛогинПароль:

2 страниц V  1 2 >  
 Ответить  Открыть новую тему 
> Алгоритмы пузырьковой сортировки
сообщение
Сообщение #1


Бывалый
***

Группа: Пользователи
Сообщений: 212
Пол: Мужской

Репутация: -  0  +


Итак, все знают что есть такой вид сортировки, как пузырьковая. Меня интересует какие еще есть алгоритмы в этом виде сортировки кроме: последовательного и чет-нечетной перестановки? И если есть информация о них поделиться ей, если не жалко.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #2


Гуру
*****

Группа: Пользователи
Сообщений: 1 013
Пол: Мужской
Ада: Разработчик
Embarcadero Delphi: Разработчик
Free Pascal: Разработчик

Репутация: -  627  +


Odd-Even Sort (так называемая "параллельная пузырьковая") интересует? Или ты ее и называешь чет-нечетной перестановкой? Сортировка расческой (вроде как тоже усовершенствование пузырька)?
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #3


Бывалый
***

Группа: Пользователи
Сообщений: 212
Пол: Мужской

Репутация: -  0  +


Первая, действительно чет-нечет, а вот вторую не видел, спасибо. Нашел еще шейкер-сортировку

Просто нужно для курсового проекта рассмотреть варианты алгоритмов пузырьковой ,выбрать наилучший по трудоемкости для дальнейшего распараллеливания вычисления данным алгоритмом)))
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #4


Бывалый
***

Группа: Пользователи
Сообщений: 212
Пол: Мужской

Репутация: -  0  +


Сортировка расческой (вроде как тоже усовершенствование пузырька) - что-то у меня она прооигрывает обычной сортировке. Вот как сделал
void combsort( double *pData, int DataSize )
{ double tmp;

int jump = DataSize;
bool swapped = true;

while (jump > 0 || swapped)
{
if (jump > 0)
jump = (jump / 1.25);
swapped = false;
for (int i = 0; i + jump < DataSize-1; i++)
if(pData[i] > pData[i + 1]) {
tmp = pData[i];
pData[i] = pData[i + 1];
pData[i + 1] = tmp;swapped = true;}

}
}


Сообщение отредактировано: Account -
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #5


Гуру
*****

Группа: Пользователи
Сообщений: 1 013
Пол: Мужской
Ада: Разработчик
Embarcadero Delphi: Разработчик
Free Pascal: Разработчик

Репутация: -  627  +


Ты б лучше обе реализации привел, чтоб сравнить можно было... Лучше всего - целиком программу, которая показывает, где больше, а где - меньше времени. А то так знаешь, все может зависеть от многих факторов...

P.S. Вот тут лежит реализация "расчески на С", на кой велосипед придумывать опять? smile.gif

Сообщение отредактировано: IUnknown -
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #6


Бывалый
***

Группа: Пользователи
Сообщений: 212
Пол: Мужской

Репутация: -  0  +


IUnknown, велосипед не придумывал выдернул код представленный с первой твоей ссылки))), но он оказался медленнее представленного тобой по последней ссылке.Походу по первой не совсем верный алгоритм.

Вот прога целиком

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <ctime>

using namespace std;
void RandomDataInitialization(double *&pData, int& DataSize) ;
void ProcessInitialization(double *&pData, int& DataSize) ;
void ProcessTermination(double *pData);
void DummyDataInitialization(double*& pData, int& DataSize);
void Shaker(double *pData, int DataSize);
void combsort( double *pData, int DataSize );
void PrintData(double *pData, int DataSize);
const double RandomDataMultiplier = 1000.0;

int main(int argc, char *argv[]) {
double *pData = 0;
int DataSize = 0;

time_t start, finish;
double duration = 0.0;

printf("Serial bubble sort program\n");

// Process initialization // В этой функции можно менять вид заполнения массива
ProcessInitialization(pData, DataSize);

//printf("Data before sorting\n");
//PrintData(pData, DataSize); // Для проверки исходных данных

// Serial bubble sort // Виды сортировки, обычная пузырьковая, расческой, Шейкера активация на выбор
start = clock();
SerialBubble(pData, DataSize);
//combsort(pData, DataSize);
//Shaker(pData, DataSize);
finish = clock();

//printf("Data after sorting\n");
//PrintData(pData, DataSize); //Для проверки результата

duration = (finish - start) / double(CLOCKS_PER_SEC);
printf("Time of execution: %f\n", duration);

// Process termination
ProcessTermination(pData);

return 0;
}

// Function for allocating the memory and setting the initial values
void ProcessInitialization(double *&pData, int& DataSize) {
do {
printf("Enter the size of data to be sorted: ");
scanf("%d", &DataSize);
if(DataSize <= 0)
printf("Data size should be greater than zero\n");
}
while(DataSize <= 0);

printf("Sorting %d data items\n", DataSize);

pData = new double[DataSize];

// Simple setting the data
//DummyDataInitialization(pData, DataSize);

// Setting the data by the random generator
RandomDataInitialization(pData, DataSize);
}

// Function for computational process termination
void ProcessTermination(double *pData) {
delete []pData;
}

// Function for simple setting the initial data
void DummyDataInitialization(double*& pData, int& DataSize) {
for(int i = 0; i < DataSize; i++)
pData[i] = DataSize - i;
}

// Function for initializing the data by the random generator
void RandomDataInitialization(double *&pData, int& DataSize) {
srand( (unsigned)time(0) );

for(int i = 0; i < DataSize; i++)
pData[i] = double(rand()) / RAND_MAX * RandomDataMultiplier;
}

// Serial bubble sort algorithm
void SerialBubble(double *pData, int DataSize) {
double Tmp;

for(int i = 1; i < DataSize; i++)
for(int j = 0; j < DataSize - i; j++)
if(pData[j] > pData[j + 1]) {
Tmp = pData[j];
pData[j] = pData[j + 1];
pData[j + 1] = Tmp;
}
}

void PrintData(double *pData, int DataSize) {
for(int i = 0; i < DataSize; i++)
printf("%7.4f ", pData[i]);
printf("\n");
}

void Shaker(double *pData, int DataSize){
double t;
bool exchange;

do {
exchange = false;
for(int i=DataSize-1; i > 0; --i) {
if(pData[i-1] > pData[i]) {
t = pData[i-1];
pData[i-1] = pData[i];
pData[i] = t;
exchange = true;
}
}
for(int i=1; i < DataSize; ++i) {
if(pData[i-1] > pData[i]) {
t = pData[i-1];
pData[i-1] = pData[i];
pData[i] = t;
exchange = true;
}
}
} while(exchange);

}

void combsort(double *pData, int DataSize) {
float shrink_factor = 1.247330950103979;
int gap = DataSize, swapped = 1, i; double swap;

while ((gap > 1) || swapped) {
if (gap > 1)
gap = gap / shrink_factor;

swapped = 0;
i = 0;

while ((gap + i) < DataSize) {
if (pData[i] - pData[i + gap] > 0) {
swap = pData[i];
pData[i] = pData[i + gap];
pData[i + gap] = swap;
swapped = 1;
}
++i;
}
}
}



еще надо будет добавить чет-нечет
Дальше дело за малым, прогон на проверку каждого алгоритма.

Сообщение отредактировано: Account -
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #7


Гуру
*****

Группа: Пользователи
Сообщений: 1 013
Пол: Мужской
Ада: Разработчик
Embarcadero Delphi: Разработчик
Free Pascal: Разработчик

Репутация: -  627  +


Цитата
Походу по первой не совсем верный алгоритм.
Это надо сказать спасибо переводчикам Википедии. Вот именно поэтому я и предпочитаю работать с англоязычными источниками.

Цитата
Дальше дело за малым, прогон на проверку каждого алгоритма.
Не понял. То есть, ты считаешь, что если какая-то из написанных тобой функций будет быстрее без распараллеливания, то и при параллельном выполнении тоже она же будет быстрее? Наивный такой smile.gif Ты сначала сделай параллельные версии всех этих методов, а потом сравнивай. На 2-х ядрах, на 4-х и на 8 (если есть - еще и на большем количестве). Результаты тебя заставят удивиться, я думаю...

Сообщение отредактировано: IUnknown -
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #8


Бывалый
***

Группа: Пользователи
Сообщений: 212
Пол: Мужской

Репутация: -  0  +


Цитата(IUnknown @ 24.06.2011 0:09) *

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

Не понял. То есть, ты считаешь, что если какая-то из написанных тобой функций будет быстрее без распараллеливания, то и при параллельном выполнении тоже она же будет быстрее? Наивный такой smile.gif Ты сначала сделай параллельные версии всех этих методов, а потом сравнивай. На 2-х ядрах, на 4-х и на 8 (если есть - еще и на большем количестве). Результаты тебя заставят удивиться, я думаю...

Я не считаю что она будет быстрее)))
Просто с видом сортировки как бы определилось, а вот алгоритм нужно выбрать по наименьшей трудоемкости, но опять же каждый алгоритм по разному ведет себя при разных объемах сортируемых данных, т.е. нужно построить график зависимости от кол-ва элементов ко времени, и уже отсюда плясать. Так как например грубо говоря дана задача отсортировать 100000 элементов. допустим прогоним эти алгоритмы, посмотрим результат. Для примеру допустим среднее от всех результатов алгоритмов 20 минут. Ставим задачу отсортировать за 4 минуты. т.е. создать кластер из подобных машин, на которй ставиться эксперимент, расчитать кол-во узлов кластера. Это все естетсвенно теоретически насчет вычислений параллельных, так как высчитать негде и нужно MPI изучать для реализации. курсовой проект такой дали.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #9


Бывалый
***

Группа: Пользователи
Сообщений: 212
Пол: Мужской

Репутация: -  0  +


Добавил чет-нечет и проводил эксперимент на сортировку 25000 элементов. Комбинированная всех уделала, разница во времени просто обалдеть можно.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #10


Бывалый
***

Группа: Пользователи
Сообщений: 212
Пол: Мужской

Репутация: -  0  +


Никак до конца не пойму применения чет-нечет алгоритма для параллельной обработки.
1 - сортировка на всех узлах
2-обмен соседних узлов (Везде приводиться один и тот же пример причем после 2 пункта в соседних процессах хранятся уже упорядоченный данные, а не просто обмененные.)

вот второй пункт не до конца понимаю, нигде в подробностях не описано. Как я понял, это объединения данных соседних узлов, сортировка этого объединенного массива и разбиение опять на две части.


Правильно ли я понимаю и где можно почитать в подробностях данного параллельного алгоритма?

Сообщение отредактировано: Account -
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #11


Гуру
*****

Группа: Пользователи
Сообщений: 1 013
Пол: Мужской
Ада: Разработчик
Embarcadero Delphi: Разработчик
Free Pascal: Разработчик

Репутация: -  627  +


Вот по этому разобраться сможешь?
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #12


Бывалый
***

Группа: Пользователи
Сообщений: 212
Пол: Мужской

Репутация: -  0  +


Основа понятна вот этот участок не очень особенно по распределению элементов
/* This is the CompareSplit function */ 
74 CompareSplit(int nlocal, int *elmnts, int *relmnts, int *wspace,
75 int keepsmall)
76 {
77 int i, j, k;
78
79 for (i=0; i<nlocal; i++)
80 wspace[i] = elmnts[i]; /* Copy the elmnts array into the wspace array */
81
82 if (keepsmall) { /* Keep the nlocal smaller elements */
83 for (i=j=k=0; k<nlocal; k++) {
84 if (j == nlocal || (i < nlocal && wspace[i] < relmnts[j])) //<------Не въеду что то, как в словах это правильно озвучить
85 elmnts[k] = wspace[i++];
86 else
87 elmnts[k] = relmnts[j++];
88 }
89 }
90 else { /* Keep the nlocal larger elements */
91 for (i=k=nlocal-1, j=nlocal-1; k>=0; k--) {
92 if (j == 0 || (i >= 0 && wspace[i] >= relmnts[j])) //<--------------Естественно здесь тоже
93 elmnts[k] = wspace[i--];
94 else
95 elmnts[k] = relmnts[j--];
96 }
97 }
98 }


Сообщение отредактировано: Account -
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #13


Гуру
*****

Группа: Пользователи
Сообщений: 1 013
Пол: Мужской
Ада: Разработчик
Embarcadero Delphi: Разработчик
Free Pascal: Разработчик

Репутация: -  627  +


     if (keepsmall) { /* Keep the nlocal smaller elements */ 

// Этот кусок упорядочивает в массив elmnts минимальные значения из elmnts и
// relmnts (которые получены от соседнего процесса) по возрастанию
// (сколько поместится в elmnts) Скажем, если было:
// elmnts = (27 39 54 74 90)
// relmnts = (22 38 50 65 69)
// то после выполнения нижеследующего куска кода
// elmnts = (22 27 38 39 50)

for (i=j=k=0; k<nlocal; k++) {
if (j == nlocal || (i < nlocal && wspace[i] < relmnts[j]))
elmnts[k] = wspace[i++];
else
elmnts[k] = relmnts[j++];
}
}
else { /* Keep the nlocal larger elements */
// Соответственно, этот кусок делает то же самое, но сохраняет nlocal максимальных
// элементов из совокупности elmnts и relmnts. Т.е., если
// elmnts = (16 18 47 62 70)
// relmnts = ( 1 13 23 29 92), то в результате
// elmnts = (29 47 62 70 92)
// в упорядоченном виде
for (i=k=nlocal-1, j=nlocal-1; k>=0; k--) {
if (j == 0 || (i >= 0 && wspace[i] >= relmnts[j]))
elmnts[k] = wspace[i--];
else
elmnts[k] = relmnts[j--];
}
}
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #14


Бывалый
***

Группа: Пользователи
Сообщений: 212
Пол: Мужской

Репутация: -  0  +


Цитата(IUnknown @ 27.06.2011 14:38) *

     if (keepsmall) { /* Keep the nlocal smaller elements */ 

// Этот кусок упорядочивает в массив elmnts минимальные значения из elmnts и
// relmnts (которые получены от соседнего процесса) по возрастанию
// (сколько поместится в elmnts) Скажем, если было:
// elmnts = (27 39 54 74 90)
// relmnts = (22 38 50 65 69)
// то после выполнения нижеследующего куска кода
// elmnts = (22 27 38 39 50)

for (i=j=k=0; k<nlocal; k++) {
if (j == nlocal || (i < nlocal && wspace[i] < relmnts[j]))
elmnts[k] = wspace[i++];
else
elmnts[k] = relmnts[j++];
}
}
else { /* Keep the nlocal larger elements */
// Соответственно, этот кусок делает то же самое, но сохраняет nlocal максимальных
// элементов из совокупности elmnts и relmnts. Т.е., если
// elmnts = (16 18 47 62 70)
// relmnts = ( 1 13 23 29 92), то в результате
// elmnts = (29 47 62 70 92)
// в упорядоченном виде
for (i=k=nlocal-1, j=nlocal-1; k>=0; k--) {
if (j == 0 || (i >= 0 && wspace[i] >= relmnts[j]))
elmnts[k] = wspace[i--];
else
elmnts[k] = relmnts[j--];
}
}


Правильно ли я понимаю, что
wspace[i--]//<---совокупность и elmnts и relmnts

и по сути из него выбирается опять же методом сравнения(как по твоему, если бы отсортировать его и потом просто делить, привело бы к уменьшению времени выполнения данного кода ?)

Сообщение отредактировано: Account -
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #15


Гуру
*****

Группа: Пользователи
Сообщений: 1 013
Пол: Мужской
Ада: Разработчик
Embarcadero Delphi: Разработчик
Free Pascal: Разработчик

Репутация: -  627  +


Цитата
как по твоему, если бы отсортировать его и потом просто делить, привело бы к уменьшению времени выполнения данного кода ?
С использованием MPI проверить нет возможности, а вот используя встроенную многозадачность Ады - проверил. Ночью запустил несколько тестов (все одно дежурил, делать особо нечего было, скучно smile.gif ), для 4, 8, 16 и 32-х процессов с разным количеством элементов (для простоты - кол-во элементов кратное кол-ву процессов, как и в программе по ссылке). Разницы в скорости выполнения не обнаружил. Т.е, что с CompareSplit, что с готовой сортировкой - одинаково. Это, учти, при том, что у меня есть возможность не копировать поэлементно два массива в один, а воспользоваться Array Slices, и обратно - то же самое, просто взять и вернуть часть массива, тебе же в Сях придется копировать в цикле. Можешь еще и потерять в скорости...
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #16


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


В Сях можно явно передавать то, что Ада передаёт неявно - т.е. указатель на начало и границы подмассива.
Многозадачность на скольки ядрах смотрел?


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #17


Гуру
*****

Группа: Пользователи
Сообщений: 1 013
Пол: Мужской
Ада: Разработчик
Embarcadero Delphi: Разработчик
Free Pascal: Разработчик

Репутация: -  627  +


Цитата
Многозадачность на скольки ядрах смотрел?
16-ть процессоров, 8 ядер на каждом... 128, получается...

Цитата
В Сях можно явно передавать то, что Ада передаёт неявно - т.е. указатель на начало и границы подмассива.
Смотри:

   Array_Size : constant Integer := Total_Elements / Processes_Count;

type Int_Array is array (Natural range <>) of Integer;
subtype Sub_Array is Int_Array(0 .. Array_Size - 1);

-- ...

task body Sorter is
A : Int_Array(0 .. 2 * Array_Size - 1);
begin
accept Data (velmnts : Sub_Array; vrelmnts : Sub_Array) do
A(0 .. Array_Size - 1) := velmnts; -- Особенно интересует это ...
A(Array_Size .. 2 * Array_Size - 1) := vrelmnts; -- вот это ...
end Data;

if Keep_Small then -- Это дискриминант задачи ...
Sort(A);
else
Rev_Sort(A);
end if;

accept Request (Arr : out Sub_Array) do
Arr := A(0 .. Array_Size - 1); -- ... и вот это ...
end Request;
end Sorter;
Сможешь написать аналог на С без потерь на копирование?
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #18


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Я так понял, тут фишка в том, что velmnts и vrelmnts могут храниться в совсем разных областях памяти и всё равно компилятор умеет убирать копирование?
Ну грубо говоря, можно написать класс "располовиненный массив" с перегруженным оператором []. Правда, это уже не совсем чистый Си, но на Си принцип тот же. А вообще, чё там Си, да хоть на ассемблере. Дизассембелирую твой код и посмотрю.

Другое дело, что одна и та же конструкция языка высокого уровня может в зависимости от контекста скомпилироваться в принципиально разные вещи. То есть если переписать этот пример на Си именно для располовиненного массива, то алгоритм пузырька изуродуется до неузнаваемости, и если мне понадобится подредактировать уже изуродованный алгоритм, то я тупо задолбаюсь. А если это "уродование" делает компилятор, сохраняя исходник в чистоте, то всё в порядке.

Я вот что не пойму. Ты хочешь сказать, что твой компилятор умеет анализировать всё, что будет делаться над массивом и сделать вывод о том, что его вообще есть смысл хранить по частям (отдельные половинки в разных областях памяти)? Что-то я не заметил у ГНАТа высокий уровень интеллекта (я даже не смог заставить заменить вызов функции подсчёта суммы элементов константного массива на значение). Или я не заметил какой-то простой нюанс? Или у тебя не ГНАТ, а коммерческий мегакомпилятор?


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #19


Гуру
*****

Группа: Пользователи
Сообщений: 1 013
Пол: Мужской
Ада: Разработчик
Embarcadero Delphi: Разработчик
Free Pascal: Разработчик

Репутация: -  627  +


Цитата
Я вот что не пойму. Ты хочешь сказать, что твой компилятор умеет анализировать всё, что будет делаться над массивом и сделать вывод о том, что его вообще есть смысл хранить по частям (отдельные половинки в разных областях памяти)? <...> Или у тебя не ГНАТ, а коммерческий мегакомпилятор?
Я ничего не хочу сказать, я констатирую факт: если я вызываю вместо CompareSplit готовую функцию сортировки - это фактически не влияет на скорость выполнения задачи. Как буду дежурить опять - попробую погонять для очень больших значений Total_Elements, может, там что прояснится. Да, разумеется, используется GNAT Pro (другого здесь просто нет) и, разумеется, не под Windows.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #20


Бывалый
***

Группа: Пользователи
Сообщений: 212
Пол: Мужской

Репутация: -  0  +


Вот что меня еще смущает , погонял сортировку алгоритмом расческой, так на моем ноуте Dual-Core 2.1 GHz . 2 гига ОЗУ, 100000 элементов сортирует показывает за 0.062 с. 30000000 примерно за 21 секунду.
Это реально? Просто что то сомнения гложат в этом направлении.
а так еще надумал что тогда при таких результатах можно для распараллеливания использовать чет-нечет алгоритм, а для сортировки на узлах расческой.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 

2 страниц V  1 2 >
 Ответить  Открыть новую тему 
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 




- Текстовая версия 16.08.2018 9:30
Хостинг предоставлен компанией "Веб Сервис Центр" при поддержке компании "ДокЛаб"