Сортировка методом простого двухпутевого слияния (помогите найти ошибку)

Модератор: Модераторы разделов

Аватара пользователя
Atragor
Сообщения: 681
Статус: ...

Сортировка методом простого двухпутевого слияния

Сообщение Atragor »

Код: Выделить всё

S1:    s=0; p=1;
  S2:    if (s==0) { i=1; j=N; k=N; l=2*N+1; }
         if (s==1) { i=N+1; j=2*N; k=0; l=N+1; }
         d=1; q=p; r=p;
  S3:    if (R[i]->K>R[j]->K) goto S8;
  S4:    k=k+d; R[k]=R[i];
  S5:    i+=1; q-=1; if (q>0) goto S3;
  S6:    k+=d; if (k==l) goto S13; else R[k]=R[j];
  S7:    j-=1; r-=1; if (r>0) goto S6; else goto S12;
  S8:    k+=d; R[k]=R[j];
  S9:    j-=1; r-=1; if (r>0) goto S3;
  S10:   k+=d; if (k==l) goto S13; else R[k]=R[i];
  S11:   i+=1; q-=1; if (q>0) goto S10;
  S12:   q=p; r=p; d=-d; t=k; k=l; l=t; if (j-i<p) goto S10; else goto S3;
  S13:   p+=p; if (p<N) { s=1-s; goto S2; }
         if (s==0) for (t=1; t<=N; t+=1) { R[t]=R[t+N]; }


Это алгоритм из учебника Д. Кнута. Данная конструкция работает, но работает немного не так, как хотелось бы:

Код: Выделить всё

Unsorted:
  4 8 84 32 192 7 54 1 283
  Sorted:
  4 0 1 7 8 32 54 84 192


Вместо 283 - 0, да и сортировка в начале неправильная... Я подозреваю, что где-то проблемы с индексами массива, но поскольку принцип работы алгоритма я не осилил и максимум, что у меня получилось - это переписать его, заменив goto на циклы, найти эту ошибку не могу. Поэтому прошу помочь :blush:
If you were MEANT to understand it, we wouldn't have called it 'code' © bash.org
Спасибо сказали:
un-defined
Сообщения: 145
ОС: Kubuntu, Gentoo

Re: Сортировка методом простого двухпутевого слияния

Сообщение un-defined »

Atragor писал(а):
14.02.2008 00:34
максимум, что у меня получилось - это переписать его, заменив goto на циклы


А если goto оставить - все хорошо работает?
Don`t try - just do or do not ©Master Joda
Спасибо сказали:
Аватара пользователя
Atragor
Сообщения: 681
Статус: ...

Re: Сортировка методом простого двухпутевого слияния

Сообщение Atragor »

И оригинал, и моя "модификация" работают одинаково неправильно.
If you were MEANT to understand it, we wouldn't have called it 'code' © bash.org
Спасибо сказали:
Аватара пользователя
Folderx
Сообщения: 296
ОС: fedora, mandriva

Re: Сортировка методом простого двухпутевого слияния

Сообщение Folderx »

(Atragor) писал(а):Вместо 283 - 0, да и сортировка в начале неправильная... Я подозреваю, что где-то проблемы с индексами массива


Код: Выделить всё

S1:    s=0; p=1;
S2:    if (s==0) { i=1; j=N; k=N; l=2*N+1; }
       if (s==1) { i=N+1; j=2*N; k=0; l=N+1; }
       d=1; q=p; r=p;
S3:    if (R[i]->K>R[j]->K) goto S8;


Проверь i и j, они у тебя начинаются i с 1(надо наверное с нуля сделать чтобы цифра 4 в сотритровке участвовала), а j непонятно с чего N это что(длина первой части?).

Нулевой элемент у меня возникал когда сортировка вылазила за правую границу массива(но это было в пузырьковой)

смотри

Код: Выделить всё

if (s==0) for (t=1; t<=N; t+=1) { R[t]=R[t+N]; }

чо получается R[t] всегда как минимум R[1], а как же R[0] ?
Спасибо сказали:
Аватара пользователя
Atragor
Сообщения: 681
Статус: ...

Re: Сортировка методом простого двухпутевого слияния

Сообщение Atragor »

Весь массив имеет длину 2*N, где N - число элементов, подлежащих сортировке, они изначально лежат в первой половине массива и в ходе алгоритма по определенным праилам пересылаются во вторую половину, потом обратно и т.д.
i и j я пробовал менять, но тогда вообще никак не сортирует.
If you were MEANT to understand it, we wouldn't have called it 'code' © bash.org
Спасибо сказали:
Аватара пользователя
wzrd
Сообщения: 323
ОС: Debian Lenny

Re: Сортировка методом простого двухпутевого слияния

Сообщение wzrd »

а не мог бы ты поверхностно описать алгоритм или хотя бы название. а то такое ощущение что по коду обфускатором прошлись. разбирать вообще не удобно.
Спасибо сказали:
Аватара пользователя
Atragor
Сообщения: 681
Статус: ...

Re: Сортировка методом простого двухпутевого слияния

Сообщение Atragor »

Название я указал в заголовке темы, описание длинное, есть вот тут:
http://ips.ifmo.ru/courses/course1/chE/l8/index.html#alg2

Что-то подсказывает, что придется писать с нуля, предварительно долго и упорно вникая в суть алгоритма. У нас в методичке дана блок-схема, но она, во-первых, составлена по Кнуту, а во-вторых, программа, написанная по ней, вообще не дает никаких результатов.
If you were MEANT to understand it, we wouldn't have called it 'code' © bash.org
Спасибо сказали:
Аватара пользователя
wzrd
Сообщения: 323
ОС: Debian Lenny

Re: Сортировка методом простого двухпутевого слияния

Сообщение wzrd »

спасибо за алгоритм. очень интересный. как напишу выложу код.
Спасибо сказали:
Аватара пользователя
wzrd
Сообщения: 323
ОС: Debian Lenny

Re: Сортировка методом простого двухпутевого слияния

Сообщение wzrd »

на сайте который ты мне дал описание очень плохое ИМХО. Вот ссылка где есть исходники. Сегодня начну писать и завтра покажу свой код. Единственное что мне не нравится в нем это использование рекурсивных вызовов...
Спасибо сказали:
Аватара пользователя
Atragor
Сообщения: 681
Статус: ...

Re: Сортировка методом простого двухпутевого слияния

Сообщение Atragor »

Спасибо за помощь. Алгоритм естественного слияния я написал, а вот с простым слиянием косяк, индекс в каком-то месте вылезает за пределы массива.

edit: Эврика!!!!!!!!!!!!!!!!!!!!!!!!!! В двух местах надо было переставить строчки :D
Вот рабочий код, осталось от goto избавиться...

Код:

do { if (sendingDirection) { i = 0; j = size - 1; k = size; l = 2 * size - 1; } else { i = size; j = 2 * size - 1; k = 0; l = size - 1; } writingDirection = 1; q = currentSegmentSize; r = currentSegmentSize; while (true) { if (array[i] <= array[j]) { array[k] = array[i]; if (k == l) break; k += writingDirection; i++; q--; if (q > 0) { continue; } do { array[k] = array[j]; if (k == l) { currentSegmentSize *= 2; sendingDirection = !sendingDirection; goto END; } k += writingDirection; j--; r--; } while (r > 0); } else { array[k] = array[j]; if (k == l) break; k += writingDirection; j--; r--; if (r > 0) { continue; } do { array[k] = array[i]; if (k == l) { currentSegmentSize *= 2; sendingDirection = !sendingDirection; goto END; } k += writingDirection; i++; q--; } while (q > 0); } q = currentSegmentSize; r = currentSegmentSize; writingDirection = -writingDirection; int temp = k; k = l; l = temp; } END:; } while (currentSegmentSize < size);


wzrd, еще раз спасибо!
If you were MEANT to understand it, we wouldn't have called it 'code' © bash.org
Спасибо сказали:
Аватара пользователя
wzrd
Сообщения: 323
ОС: Debian Lenny

Re: Сортировка методом простого двухпутевого слияния

Сообщение wzrd »

да не за что. я вроде особо и не успел помочь) ради интереса хочется его написать. самая объемная сортировка (по коду) из тех что я видел...
Спасибо сказали:
Аватара пользователя
Atragor
Сообщения: 681
Статус: ...

Re: Сортировка методом простого двухпутевого слияния

Сообщение Atragor »

Выяснил неприятную вещь :dry: Он работает только на массивах с длиной, равной степени двойки. Надо где-то поправлять...
If you were MEANT to understand it, we wouldn't have called it 'code' © bash.org
Спасибо сказали:
Аватара пользователя
wzrd
Сообщения: 323
ОС: Debian Lenny

Re: Сортировка методом простого двухпутевого слияния

Сообщение wzrd »

Atragor писал(а):
16.02.2008 19:48
Выяснил неприятную вещь :dry: Он работает только на массивах с длиной, равной степени двойки. Надо где-то поправлять...

может быть кратное двум, а не степень двойки? блин, ни как руки не доходят заняться... чтобы написать алгоритм простых слияний, нужно знать алгоритм естественного слияния? какая между ними связь?
Спасибо сказали:
Аватара пользователя
Atragor
Сообщения: 681
Статус: ...

Re: Сортировка методом простого двухпутевого слияния

Сообщение Atragor »

Именно степеням двойки. Я написал оба, чтобы лучше разобраться, ну и в описаниях обычно рассказывают о простом, основываясь на естественном.
Цитата из Кнута по поводу алгоритма:
Довольно удивительно, что этот метод работает и тогда, когда N не является степенью 2.


То есть работать должен для любых длин...
If you were MEANT to understand it, we wouldn't have called it 'code' © bash.org
Спасибо сказали:
Аватара пользователя
wzrd
Сообщения: 323
ОС: Debian Lenny

Re: Сортировка методом простого двухпутевого слияния

Сообщение wzrd »

хоть как то разобрался с алгоритмом... сам столкнулся с проблемой связанной скорее с языком.
вот код:

Код:

#include <fstream> using namespace std; void merge (int *, int , int *, int , int *, int); int *mergesort (int *, int); int main () { ifstream fin ("a.in"); ofstream fout ("a.out"); int n; fin>>n; int i; int *arr=new int [n]; for (i=0;i<n;i++){ fin>>arr [i]; } int *res = mergesort (arr,n); for (i=0;i<m+n;i++){ fout<<res[i]<<" "; } fout.close (); fin.close (); return 0; } void merge (int *arr, int lena, int *brr, int lenb, int *res, int len) { int i, j, k; i=j=k=0; int temp; while (k<len){ if (i==lena) temp=brr[j]; else if (j==lenb) temp=arr[i]; else temp=(arr[i]>brr[j])?brr[j]:arr[i]; res[k]=temp; if (temp==arr[i]) ++i; else ++j; ++k; } } int *mergesort (int *arr, int len) { if (len==1) return arr; else { int middle=len/2; int buff [1000]; merge(mergesort (&arr[middle],middle), middle, mergesort (arr,middle), middle, buff, len); return buff; } }

как видно в последней функии возразается адрес локальной памяти, это грубая ошибка-понимаю. но как мне еще реализовать обмен адресами (массивами)? глобальные переменные не предлогать, мне не интересно такое решение. как возвращать значения или где хранить? мне бы интересно было узнать кто как бы написал этот алгоритм (не в плане алгоритма, а в плане выделений памяти и т.д.). как вообще принято выделять память и работать с ней (как бы стандарт), может кто знает ссыку на такой материал?
Спасибо сказали:
Аватара пользователя
ZyX
Сообщения: 355
ОС: Gentoo

Re: Сортировка методом простого двухпутевого слияния

Сообщение ZyX »

wzrd писал(а):
17.02.2008 17:00
как видно в последней функии возразается адрес локальной памяти, это грубая ошибка-понимаю. но как мне еще реализовать обмен адресами (массивами)? глобальные переменные не предлогать, мне не интересно такое решение. как возвращать значения или где хранить? мне бы интересно было узнать кто как бы написал этот алгоритм (не в плане алгоритма, а в плане выделений памяти и т.д.). как вообще принято выделять память и работать с ней (как бы стандарт), может кто знает ссыку на такой материал?
Насчет стандарта не знаю, но одномерные массивы заменяются на такие конструкции без переписки кода (внимание - стиль C!):

Код:

/*int a[N]*/ int *a; if( !(a= /* malloc возвращает 0, * если память выделить не * удалось, а 0 - это ЛОЖЬ */ (int *) /* malloc всегда возвращает * void *, на что компилятор * иногда выдает предупреждение */ malloc(N*sizeof(int)) /* malloc принимает * размер в байтах * в качестве аргумента, * а размер типа почти * всегда больше 1 байта */ ) { puts ("Не удалось выделить память под a"); /* обработать ошибку, не обязательно через exit. * Главное - исключить обращения к этой переменной */ exit (1); } /* Версия без комментариев: */ int *a; if(!(a=(int *) malloc(N*sizeof(int))) { puts ("Не удалось выделить память под a"); exit (1); }
Надеюсь, комментарии не слишком развернутые.
malloc находится в stdlib.h, exit - там же.
Решение вроде работает и в C++, хотя и имеет старый стиль, как пользоваться new пускай другие говорят.
Спасибо сказали:
Аватара пользователя
wzrd
Сообщения: 323
ОС: Debian Lenny

Re: Сортировка методом простого двухпутевого слияния

Сообщение wzrd »

ZyX писал(а):
19.03.2008 01:50
wzrd писал(а):
17.02.2008 17:00
как видно в последней функии возразается адрес локальной памяти, это грубая ошибка-понимаю. но как мне еще реализовать обмен адресами (массивами)? глобальные переменные не предлогать, мне не интересно такое решение. как возвращать значения или где хранить? мне бы интересно было узнать кто как бы написал этот алгоритм (не в плане алгоритма, а в плане выделений памяти и т.д.). как вообще принято выделять память и работать с ней (как бы стандарт), может кто знает ссыку на такой материал?
Насчет стандарта не знаю, но одномерные массивы заменяются на такие конструкции без переписки кода (внимание - стиль C!):

Код:

/*int a[N]*/ int *a; if( !(a= /* malloc возвращает 0, * если память выделить не * удалось, а 0 - это ЛОЖЬ */ (int *) /* malloc всегда возвращает * void *, на что компилятор * иногда выдает предупреждение */ malloc(N*sizeof(int)) /* malloc принимает * размер в байтах * в качестве аргумента, * а размер типа почти * всегда больше 1 байта */ ) { puts ("Не удалось выделить память под a"); /* обработать ошибку, не обязательно через exit. * Главное - исключить обращения к этой переменной */ exit (1); } /* Версия без комментариев: */ int *a; if(!(a=(int *) malloc(N*sizeof(int))) { puts ("Не удалось выделить память под a"); exit (1); }
Надеюсь, комментарии не слишком развернутые.
malloc находится в stdlib.h, exit - там же.
Решение вроде работает и в C++, хотя и имеет старый стиль, как пользоваться new пускай другие говорят.

спасибо, постепенно избавляюсь от потоков, а щас избавлюсь от new. а какие системные вызовы отвечают за выделение памяти в никсаx?
Спасибо сказали:
DaemonFree
Сообщения: 5
ОС: FreeBSD

Re: Сортировка методом простого двухпутевого слияния

Сообщение DaemonFree »

Поищи хидер в /sys/src/ sys_call В нем есть все системные вызовы с комментариями. А на вскидку не помню, уж извини ;)
Спасибо сказали: