Метод за оптимизация за намиране на плаващ статус на обект

Проблемът, който трябва да се реши, е да се намери състоянието на плаване на плаващо тяло, като се има предвид неговото тегло и център на тежестта.

Функцията, която използвам, изчислява изместения обем и центъра на подкосяване на тялото при дадено потъване, пета и подстригване. Където потъването е единица за дължина, а петата/подстригването е ъгъл, ограничен до стойност от -90 до 90.

Илюстрация на трите променливи

Състоянието на плаване се установява, когато изместеният обем е равен на теглото и центърът на тежестта е във вертикална линия с центъра на подскачане.

Имам това имплементирано като нелинеен проблем за намиране на корен на Нютон-Рафсон с 3 променливи (потъване, подстригване, пета) и 3 уравнения. Този метод работи, но изисква добри първоначални предположения. Така че се надявам да намеря или по-добър подход за това, или добър метод за намиране на първоначалните стойности.

По-долу е кодът за алгоритъма на Нютон и Якобиан, използван за итерацията на Нютон-Рафсън. Обемът на функцията приема параметрите потъване, пета и подстригване. И връща обема и координатите за центъра на плаваемостта.

Включих също алгоритмите maxabs и GSolve2, вярвам, че те са взети от Numerical Recipies.

void jacobian(float x[], float weight, float vcg, float tcg, float lcg, float jac[][3], float f0[]) {
    float h = 0.0001f;
    float temp;
    float j_volume, j_vcb, j_lcb, j_tcb;
    float f1[3];

    volume(x[0], x[1], x[2], j_volume, j_lcb, j_vcb, j_tcb);
    f0[0] = j_volume-weight;
    f0[1] = j_tcb-tcg;
    f0[2] = j_lcb-lcg;

    for (int i=0;i<3;i++) {
        temp = x[i];
        x[i] = temp + h;
        volume(x[0], x[1], x[2], j_volume, j_lcb, j_vcb, j_tcb);

        f1[0] = j_volume-weight;
        f1[1] = j_tcb-tcg;
        f1[2] = j_lcb-lcg;
        x[i] = temp;

        jac[0][i] = (f1[0]-f0[0])/h;
        jac[1][i] = (f1[1]-f0[1])/h;
        jac[2][i] = (f1[2]-f0[2])/h;
    }
}


void newton(float weight, float vcg, float tcg, float lcg, float &sinkage, float &heel, float &trim) {
    float x[3] = {10,1,1};

    float accuracy = 0.000001f;
    int ntryes = 30;
    int i = 0;
    float jac[3][3];
    float max;
    float f0[3];
    float gauss_f0[3];

    while (i < ntryes) {
        jacobian(x, weight, vcg, tcg, lcg, jac, f0);

        if (sqrt((f0[0]*f0[0]+f0[1]*f0[1]+f0[2]*f0[2])/2) < accuracy) {
            break;
        }

        gauss_f0[0] = -f0[0];
        gauss_f0[1] = -f0[1];
        gauss_f0[2] = -f0[2];

        GSolve2(jac, 3, gauss_f0);

        x[0] = x[0]+gauss_f0[0];
        x[1] = x[1]+gauss_f0[1];
        x[2] = x[2]+gauss_f0[2];

        // absmax(x) - Return absolute max value from an array
        max = absmax(x);
        if (max < 1) max = 1;

        if (sqrt((gauss_f0[0]*gauss_f0[0]+gauss_f0[1]*gauss_f0[1]+gauss_f0[2]*gauss_f0[2])) < accuracy*max) {
            x[0]=x2[0];
            x[1]=x2[1];
            x[2]=x2[2];
            break;
        }

        i++;
    }

    sinkage = x[0];
    heel = x[1];
    trim = x[2];
}

int GSolve2(float a[][3],int n,float b[]) {
    float x,sum,max,temp;
    int i,j,k,p,m,pos;

    int nn = n-1; 

    for (k=0;k<=n-1;k++)
    {
        /* pivot*/
        max=fabs(a[k][k]);
        pos=k;


        for (p=k;p<n;p++){
            if (max < fabs(a[p][k])){
                max=fabs(a[p][k]);
                pos=p;
            }
        }

        if (ABS(a[k][pos]) < EPS) {
            writeLog("Matrix is singular");
            break;
        }

        if (pos != k) {
            for(m=k;m<n;m++){
                temp=a[pos][m];
                a[pos][m]=a[k][m];
                a[k][m]=temp;
            }
        }

        /*   convert to upper triangular form */
        if ( fabs(a[k][k])>=1.e-6)
        {
            for (i=k+1;i<n;i++)
            {
            x = a[i][k]/a[k][k];
            for (j=k+1;j<n;j++) a[i][j] = a[i][j] -a[k][j]*x;
            b[i] = b[i] - b[k]*x;
            }
        }
        else
        {
            writeLog("zero pivot found in line:%d",k);
            return 0;
       }
     }

/*   back substitution */
     b[nn] = b[nn] / a[nn][nn];
     for (i=n-2;i>=0;i--)
     {
        sum = b[i];
         for (j=i+1;j<n;j++) 
           sum = sum - a[i][j]*b[j];
        b[i] = sum/a[i][i];
     }
     return 0;
}

float absmax(float x[]) {
    int i = 1;
    int n = sizeof(x);
    float max = x[0];
    while (i < n) {
        if (max < x[i]) {
            max = x[i];
        }
        i++;
    }
    return max;
}

person user978281    schedule 27.01.2013    source източник
comment
Имате ли достъп до програмата за решаване на нелинейна оптимизация KNITRO (академик ли сте)? Има опция за мултистарт. Може да опитате това и да видите как работи multistart, преди да пуснете свой собствен метод.   -  person user327301    schedule 27.01.2013
comment
Не съм академик, така че за съжаление нямам достъп до KNITRO.   -  person user978281    schedule 29.01.2013
comment
user978281 - Дадохте недостатъчно подробности, за да може да се отговори на въпроса ви с големи подробности. Можете ли да дадете примерен код? Ако можехме да видим вашия нелинеен NR метод, щеше да е много по-ясно как може да се приложи оптимизацията.   -  person Mike Vella    schedule 30.01.2013
comment
@user978281, подкрепям коментара на Майк Вела. Особено ако поставите награда за въпроса си, трябва да поставите възможно най-много подходящи подробности във въпроса си.   -  person user327301    schedule 30.01.2013
comment
Актуализиран с примерен код, надявам се, че това дава някои допълнителни подробности   -  person user978281    schedule 30.01.2013


Отговори (2)


Обмисляли ли сте някои стохастични методи за търсене, за да намерите първоначалната стойност и след това фина настройка с Newton Raphson? Една възможност е еволюционно изчисление, можете да използвате пакета Inspyred. За физически проблем, подобен по много начини на този, който описвате, вижте този пример: http://inspyred.github.com/tutorial.html#lunar-explorer

person Mike Vella    schedule 27.01.2013

Какво ще кажете за използването на заглушена версия на метода на Нютон? Можете доста лесно да промените внедряването си, за да го направите. Мислете за метода на Нютон като за намиране на посока

d_k = f(x_k) / f'(x_k)

и актуализиране на променливата

x_k+1 = x_k - L_k d_k

В обичайния метод на Нютон L_k винаги е 1, но това може да доведе до превишаване или превишаване. Така че нека вашият метод избере L_k. Да предположим, че вашият метод обикновено превишава. Възможна стратегия се състои в вземане на най-големия L_k в набора {1,1/2,1/4,1/8,... L_min}, така че условието

|f(x_k+1)| <= (1-L_k/2) |f(x_k)|

е удовлетворено (или L_min, ако нито една от стойностите не отговаря на този критерий).

Със същите критерии друга възможна стратегия е да започнете с L_0=1 и ако критериите не са изпълнени, опитайте с L_0/2, докато заработи (или докато L_0 = L_min). След това за L_1 започнете с min(1, 2L_0) и направете същото. След това започнете с L_2=min(1, 2L_1) и така нататък.

Между другото: сигурни ли сте, че вашият проблем има уникално решение? Предполагам, че отговорът на този въпрос зависи от формата на вашия обект. Ако имате топка за ръгби, има един ъгъл, който не можете да коригирате. Така че, ако вашата форма е близо до такъв обект, няма да се изненадам, че проблемът е труден за решаване за този ъгъл.

person Dr_Sam    schedule 31.01.2013