Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Правильно построить цикл Repeat-Until  (Прочитано 7633 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Ягодка
Читатель

ru
Offline Offline

« : 16-02-2013 10:46 » 

Добрый день, уважаемые форумчане! Да-да
В моей программе из массивов Kol, Gar, Rem вычитаются массивы m1 и m2, после этого действия получаются массивы Dm1 и Dm2. После того, как мы получили массивы Dm1 и Dm2, массивам In1 и In2 присваиваются нули и единицы, если выполняется условие "Dm1<Dm2". Ищется ошибка "Е1" - сумма квадратов массивов Dm1 и Dm2. Затем массивы m1 и m2 пересчитываются и снова вычитаются из массивов Kol, Gar, Rem и снова ищется ошибка "Е".
Самая первая ошибка "Е1" получается очень большой. А мне нужно, чтобы поиск новых значений массивов m1 и m2 и вычитание из массивов Kol, Gar, Rem массивов m1 и m2 продолжалось до тех пор, пока ошибка "Е" не станет меньше самой первой ошибки "Е1" в тысячу раз.
Т.е., при самом первом вычитании из массивов Kol, Gar, Rem массивов m1 и m2 мы получили ошибку "Е1"=272. Вычислили новые значения массивов m1 и m2, отняли эти значения из массивов Kol, Gar, Rem, посчитали массивы Dm1 и Dm2, присвоили массивам In1 и In2 нули и единицы, считаем ошибку "Е". Если не выполняется условие E<E1/1000, то мы снова ищем новые значения массивов m1 и m2, отнимаем, получаем массивы Dm1 и Dm2, присваиваем массивам In1 и In2 нули и единицы, считаем ошибку "Е", и так далее. Это я реализую через цикл Repeat-Until. У меня не получается правильно построить этот цикл, программа зависает (зацикливается). Я думаю, это потому что я вычитаю из массивов Kol, Gar, Rem не новые найденные значения массивов m1 и m2, а старые, полученные в самом начале программы с помощью поиска максимумов и минимумов... Пробовала в Repeat-Until переименовывать массивы m1 и m2 - всё равно зацикливается... Жаль
 В архиве прилагается весь проект, текст программы приведён ниже
Код:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Grids, StdCtrls, Buttons;

type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    BitBtn1: TBitBtn;
    StringGrid2: TStringGrid;
    Label1: TLabel;
    Label2: TLabel;
    StringGrid3: TStringGrid;
    Label3: TLabel;
    StringGrid4: TStringGrid;
    Label4: TLabel;
    StringGrid5: TStringGrid;
    Label5: TLabel;
    StringGrid6: TStringGrid;
    StringGrid7: TStringGrid;
    Label6: TLabel;
    Label7: TLabel;
    StringGrid8: TStringGrid;
    StringGrid9: TStringGrid;
    Label8: TLabel;
    Label9: TLabel;
    StringGrid10: TStringGrid;
    StringGrid11: TStringGrid;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    StringGrid12: TStringGrid;
    Label14: TLabel;
    Label15: TLabel;
    Label16: TLabel;
    Label19: TLabel;
    Label20: TLabel;
    procedure BitBtn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
implementation

{$R *.dfm}

procedure TForm1.BitBtn1Click(Sender: TObject); {кнопка "Кластеризация"}
var
Kol: array[1..10] of real; {Общее количество техники, которую предоставил поставщик}
Gar:  array[1..10] of real; {Общая продолжительность предоставленной гарантии от поставщика в годах}
Rem: array[1..10] of real; {Общая сумма денег, потраченных на ремонт оборудования в тысячах}
m1, m2: array [1..3] of real; {центроиды}
x1,y1,z1, x2,y2,z2: real; {координаты начальных точек центроидов}
Dm1, Dm2: array[1..10] of  real; {Евклидово расстояние между кластерами}
In1: array[1..10] of real; {Входит ли поставщик в кластер 1?}
In2: array[1..10] of real; {Входит ли поставщик в кластер 2?}
E1: real; {ошибка Е начальная}
E: real; {ошибка Е конечная}
ed1, ed2: integer; {количество 0 и 1 в массивах вхождения/невхождения}
sumKol1, sumGar1, sumRem1, sumKol2, sumGar2, sumRem2: real; {суммы массивов "количество", "гарантия", "ремонт"}
i: integer; {переменная для цикла}

begin
for i:=1 to 10 do begin
Kol[i]:=StrToFloat(StringGrid1.Cells[0,i-1]); {ввод элементов массива "количество"}
Gar[i]:=StrToFloat(StringGrid2.Cells[0,i-1]); {ввод элементов массива "продолжительность гарантии"}
Rem[i]:=StrToFloat(StringGrid3.Cells[0,i-1]); {ввод элементов массива "стоимость ремонта"}
end;

x1:=Kol[1]; {пусть 1ый элемент массива максимальный}
y1:=Gar[1];
z1:=Rem[1];
x2:=Kol[1]; {пусть 1ый элемент массива минимальный}
y2:=Gar[1];
z2:=Rem[1];

for i:=1 to 10 do begin {поиск максимумов и минимумов в массивах для координат начальных центроидов}
if Kol[i]>x1 then  {координаты для m1-надёжный поставщик}
x1:=Kol[i];
if Gar[i]>y1 then
y1:=Gar[i];
if Rem[i]<z1 then
z1:=Rem[i];
if Kol[i]<x2 then  {координаты для m2-ненадёжный поставщик}
x2:=Kol[i];
if Gar[i]<y2 then
y2:=Gar[i];
if Rem[i]>z2 then
z2:=Rem[i];
end;

m1[1]:=x1;
m1[2]:=y1;
m1[3]:=z1;
m2[1]:=x2;
m2[2]:=y2;
m2[3]:=z2;

for i:=1 to 3 do begin
StringGrid4.Cells[i-1,0]:=FloatToStr(m1[i]); {надёжный}
StringGrid5.Cells[i-1,0]:=FloatToStr(m2[i]); {ненадёжный}
end;

for i:=1 to 10 do begin
Dm1[i]:=sqrt(sqr((Kol[i]-m1[1]))+sqr((Gar[i]-m1[2]))+sqr((Rem[i]-m1[3]))); {подсчет Евклидова расстояния между кластерами}
Dm2[i]:=sqrt(sqr((Kol[i]-m2[1]))+sqr((Gar[i]-m2[2]))+sqr((Rem[i]-m2[3]))); {подсчет Евклидова расстояния между кластерами}
E1:=sqr(Dm1[i]+Dm2[i]); {ошибка Е начальная}
if Dm1[i]<Dm2[i] then  {если Евклидово расстояние, найденное от первого центроида, меньше, чем Евклидово расстояние, найденное от второго центроида}
In1[i]:=1 else
In1[i]:=0;      {элемент не входит в первый кластер, присваиваем ему 0-ложь}
if Dm1[i]<Dm2[i] then
In2[i]:=0 else
In2[i]:=1;
end;
Label13.Caption:=FloatToStrF(E1,ffFixed,5,3); {вывод ошибки Е начальной}

Repeat
ed1:=0; {обнуление количества единиц в массиве In1 - для рассчета новых координат центроида m1}
ed2:=0; {обнуление количества единиц в массиве In2 - для рассчета новых координат центроида m2}
sumKol1:=0;
sumGar1:=0;
sumRem1:=0;
sumKol2:=0;
sumGar2:=0;
sumRem2:=0;
for i:=1 to 10 do begin
Dm1[i]:=sqrt(sqr((Kol[i]-m1[1]))+sqr((Gar[i]-m1[2]))+sqr((Rem[i]-m1[3]))); {подсчет Евклидова расстояния между кластерами}
Dm2[i]:=sqrt(sqr((Kol[i]-m2[1]))+sqr((Gar[i]-m2[2]))+sqr((Rem[i]-m2[3]))); {подсчет Евклидова расстояния между кластерами}
E:=sqr(Dm1[i]+Dm2[i]); {ошибка Е конечная}
if Dm1[i]<Dm2[i] then  {если Евклидово расстояние, найденное от первого центроида, меньше, чем Евклидово расстояние, найденное от второго центроида}
In1[i]:=1 else
In1[i]:=0;      {элемент не входит в первый кластер, присваиваем ему 0-ложь}
if Dm1[i]<Dm2[i] then
In2[i]:=0 else
In2[i]:=1;
end;

for i:=1 to 10 do begin
if In1[i]=1 then begin
ed1:=ed1+1;
sumKol1:=sumKol1+Kol[i];
sumGar1:=sumGar1+Gar[i];
sumRem1:=sumRem1+Rem[i];
m1[1]:=sumKol1/ed1; {новые координаты центроида m1}
m1[2]:=sumGar1/ed1;
m1[3]:=sumRem1/ed1;
end;
if In2[i]=1 then begin
ed2:=ed2+1;
sumKol2:=sumKol2+Kol[i];
sumGar2:=sumGar2+Gar[i];
sumRem2:=sumRem2+Rem[i];
m2[1]:=sumKol2/ed2; {новые координаты центроида m1}
m2[2]:=sumGar2/ed2;
m2[3]:=sumRem2/ed2;
end;
end;
Until (E<E1/1000);

for i:=1 to 10 do begin
StringGrid12.Cells[0,i-1]:=IntToStr(i); {присвоение поставщикам свойства "надёжный" или "ненадёжный"}
if In1[i]=1 then
StringGrid12.Cells[1,i-1]:='Надёжный'
else
StringGrid12.Cells[1,i-1]:='Ненадёжный';
Label20.Caption:=FloatToStrF(E,ffFixed,5,3); {вывод результата ошибки Е конечной}
StringGrid6.Cells[0,i-1]:=FloatToStrF(Dm1[i],ffFixed,10,3);  {вывод результатов Евклидова расстояния в таблицу}
StringGrid7.Cells[0,i-1]:=FloatToStrF(Dm2[i],ffFixed,10,3);  {вывод результатов Евклидова расстояния в таблицу}
StringGrid8.Cells[0,i-1]:=FloatToStr(In1[i]); {вывод результатов вхождения/невхождения элементов в кластер 1}
StringGrid9.Cells[0,i-1]:=FloatToStr(In2[i]);  {вывод результатов вхождения/невхождения элементов в кластер 2}
end;

for i:=1 to 3 do begin
StringGrid10.Cells[i-1,0]:=FloatToStr(m1[i]); {вывод конечных координат центроида m1}
StringGrid11.Cells[i-1,0]:=FloatToStr(m2[i]); {вывод конечных координат центроида m2}
end;
end;

end.

* k-means1.rar (200.27 Кб - загружено 779 раз.)
Записан
Sla
Модератор

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 16-02-2013 10:49 » 

Ягодка, Потрудитесь отформатировать код
Такую портянку никто читать не будет
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Вад
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 18-02-2013 10:41 » 

Всю портянку не читал, но зацикливается очевидно потому, что решение не сходится. А вот почему оно не сходится...
Либо ошибка где-то в вычислении очередного приближения, либо просто не найти настолько более оптимальное решение (с в 1000 раз меньшей ошибкой).
Если это k-means (я не вникал) - то он может быстро попасть в локальный минимум.

Я бы, по крайней мере, проверял не E < E1/1000, а abs(E-Eprev) < Eprev/1000 - где Eprev - результат с предыдущей итерации. Думаю, куда лучше проверять не абсолютное изменение ошибки, а то, насколько её удалось уменьшить за последний шаг, чтобы поймать момент, когда достигнут локальный минимум, и дальше улучшения разве что микроскопические.
Записан
Ягодка
Читатель

ru
Offline Offline

« Ответ #3 : 19-02-2013 14:02 » 

И как же я узнаю результат предыдущей итерации?
Записан
Sla
Модератор

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #4 : 19-02-2013 16:49 » 

Ягодка, потрудись отформатировать код.

зы еще одна такая просьба и тема уйдет в мусорку - не обижайся.
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Вад
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 19-02-2013 17:00 » 

И как же я узнаю результат предыдущей итерации?
Разумеется, сохранять его надо. А так - оно же в переменной E уже практически лежит.
И, в целом, для того, чтобы понять, что именно происходит в программе, и всё ли там происходит как надо, порекомендую отлаживать по шагам. Причём, для начала на наборе из, скажем, 3 точек, чтобы меньше путаницы было.
Записан
Ягодка
Читатель

ru
Offline Offline

« Ответ #6 : 20-02-2013 06:42 » new

Спасибо! Прыгаю
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines