1. 程式人生 > >多邊形區域填充演算法--掃描線填充演算法(有序邊表法)

多邊形區域填充演算法--掃描線填充演算法(有序邊表法)

二、掃描線演算法(Scan-Line Filling)

        掃描線演算法適合對向量圖形進行區域填充,只需要直到多邊形區域的幾何位置,不需要指定種子點,適合計算機自動進行圖形處理的場合使用,比如電腦遊戲和三維CAD軟體的渲染等等。

        對向量多邊形區域填充,演算法核心還是求交。《計算幾何與圖形學有關的幾種常用演算法》一文給出了判斷點與多邊形關係的演算法――掃描交點的奇偶數判斷演算法,利用此演算法可以判斷一個點是否在多邊形內,也就是是否需要填充,但是實際工程中使用的填充演算法都是隻使用求交的思想,並不直接使用這種求交演算法。究其原因,除了演算法效率問題之外,還存在一個光柵圖形裝置和向量之間的轉換問題。比如某個點位於非常靠近邊界的臨界位置,用向量演算法判斷這個點應該是在多邊形內,但是光柵化後,這個點在光柵圖形裝置上看就有可能是在多邊形外邊(向量點沒有大小概念,光柵圖形裝置的點有大小概念),因此,適用於向量圖形的填充演算法必須適應光柵圖形裝置。

2.1掃描線演算法的基本思想

        掃描線填充演算法的基本思想是:用水平掃描線從上到下(或從下到上)掃描由多條首尾相連的線段構成的多邊形,每根掃描線與多邊形的某些邊產生一系列交點。將這些交點按照x座標排序,將排序後的點兩兩成對,作為線段的兩個端點,以所填的顏色畫水平直線。多邊形被掃描完畢後,顏色填充也就完成了。掃描線填充演算法也可以歸納為以下4個步驟:

(1)       求交,計算掃描線與多邊形的交點

(2)       交點排序,對第2步得到的交點按照x值從小到大進行排序;

(3)       顏色填充,對排序後的交點兩兩組成一個水平線段,以畫線段的方式進行顏色填充;

(4)       是否完成多邊形掃描?如果是就結束演算法,如果不是就改變掃描線,然後轉第1步繼續處理;

        整個演算法的關鍵是第1步,需要用盡量少的計算量求出交點,還要考慮交點是線段端點的特殊情況,最後,交點的步進計算最好是整數,便於光柵裝置輸出顯示。

        對於每一條掃描線,如果每次都按照正常的線段求交演算法進行計算,則計算量大,而且效率底下,如圖(6)所示:

圖(6) 多邊形與掃描線示意圖

觀察多邊形與掃描線的交點情況,可以得到以下兩個特點:

(1)       每次只有相關的幾條邊可能與掃描線有交點,不必對所有的邊進行求交計算;

(2)       相鄰的掃描線與同一直線段的交點存在步進關係,這個關係與直線段所在直線的斜率有關;

        第一個特點是顯而易見的,為了減少計算量,掃描線演算法需要維護一張由“活動邊”組成的表,稱為“活動邊表(AET)”。例如掃描線4的“活動邊表”由P1P2和P3P4兩條邊組成,而掃描線7的“活動邊表”由P1P2、P6P1、P5P6和P4P5四條邊組成。

        第二個特點可以進一步證明,假設當前掃描線與多邊形的某一條邊的交點已經通過直線段求交演算法計算出來,得到交點的座標為(x, y),則下一條掃描線與這條邊的交點不需要再求交計算,通過步進關係可以直接得到新交點座標為(x + △x, y + 1)。前面提到過,步進關係△x是個常量,與直線的斜率有關,下面就來推導這個△x。

        假設多邊形某條邊所在的直線方程是:ax + by + c = 0,掃描線yi和下一條掃描線yi+1與該邊的兩個交點分別是(xi,yi)和(xi+1,yi+1),則可得到以下兩個等式:

axi + byi + c = 0                        (等式 1)

axi+1 + byi+1 + c = 0                     (等式 2)

由等式1可以得到等式3:

xi = -(byi + c) / a                           (等式 3)

同樣,由等式2可以得到等式4:

xi+1 = -(byi+1 + c) / a                      (等式 4)

由等式 4 – 等式3可得到

xi+1 – xi = -b (yi+1 - yi) / a

由於掃描線存在yi+1 = yi + 1的關係,將代入上式即可得到:

xi+1 – xi = -b / a

即△x = -b / a,是個常量(直線斜率的倒數)。

        “活動邊表”是掃描線填充演算法的核心,整個演算法都是圍繞者這張表進行處理的。要完整的定義“活動邊表”,需要先定義邊的資料結構。每條邊都和掃描線有個交點,掃描線填充演算法只關注交點的x座標。每當處理下一條掃描線時,根據△x直接計算出新掃描線與邊的交點x座標,可以避免複雜的求交計算。一條邊不會一直待在“活動邊表”中,當掃描線與之沒有交點時,要將其從“活動邊表”中刪除,判斷是否有交點的依據就是看掃描線y是否大於這條邊兩個端點的y座標值,為此,需要記錄邊的y座標的最大值。根據以上分析,邊的資料結構可以定義如下:

65 typedef struct tagEDGE

66 {

67     double xi;

68     double dx;

69     int ymax;

74 }EDGE;

 根據EDGE的定義,掃描線4和掃描線7的“活動邊表”就分別如圖(7)和圖(8)所示:

 

 圖(7) 掃描線4的活動邊表

 圖(8) 掃描線7的活動邊表

        前面提到過,掃描線演算法的核心就是圍繞“活動邊表(AET)”展開的,為了方便活性邊表的建立與更新,我們為每一條掃描線建立一個“新邊表(NET)”,存放該掃描線第一次出現的邊。當演算法處理到某條掃描線時,就將這條掃描線的“新邊表”中的所有邊逐一插入到“活動邊表”中。“新邊表”通常在演算法開始時建立,建立“新邊表”的規則就是:如果某條邊的較低端點(y座標較小的那個點)的y座標與掃描線y相等,則該邊就是掃描線y的新邊,應該加入掃描線y的“新邊表”。上例中各掃描線的“新邊表”如下圖所示:

圖(9) 各掃描線的新邊表

        討論完“活動邊表(AET)”和“新邊表(NET)”,就可以開始演算法的具體實現了,但是在進一步詳細介紹實現演算法之前,還有以下幾個關鍵的細節問題需要明確:

(1)      多邊形頂點處理

        在對多邊形的邊進行求交的過程中,在兩條邊相連的頂點處會出現一些特殊情況,因為此時兩條邊會和掃描線各求的一個交點,也就是說,在頂點位置會出現兩個交點。當出現這種情況的時候,會對填充產生影響,因為填充的過程是成對選擇交點的過程,錯誤的計算交點個數,會造成填充異常。

        假設多邊形按照頂點P1、P2和P3的順序產生兩條相鄰的邊,P2就是所說的頂點。多邊形的頂點一般有四種情況,如圖(10)所展示的那樣,分別被稱為左頂點、右頂點、上頂點和下頂點:

圖(10) 多邊形頂點的四種類型

左頂點――P1、P2和P3的y座標滿足條件 :y1 < y2 < y3;

右頂點――P1、P2和P3的y座標滿足條件 :y1 > y2 > y3;

上頂點――P1、P2和P3的y座標滿足條件 :y2 > y1 && y2 > y3;

下頂點――P1、P2和P3的y座標滿足條件 :y2 < y1 && y2 < y3;

        對於左頂點和右頂點的情況,如果不做特殊處理會導致奇偶奇數錯誤,常採用的修正方法是修改以頂點為終點的那條邊的區間,將頂點排除在區間之外,也就是刪除這條邊的終點,這樣在計算交點時,就可以少計算一個交點,平衡和交點奇偶個數。結合前文定義的“邊”資料結構:EDGE,只要將該邊的ymax修改為ymax – 1就可以了。

        對於上頂點和下頂點,一種處理方法是將交點計算做0個,也就是修正兩條邊的區間,將交點從兩條邊中排除;另一種處理方法是不做特殊處理,就計算2個交點,這樣也能保證交點奇偶個數平衡。

(2)      水平邊的處理

    水平邊與掃描線重合,會產生很多交點,通常的做法是將水平邊直接畫出(填充),然後在後面的處理中就忽略水平邊,不對其進行求交計算。

(3)      如何避免填充越過邊界線

        邊界畫素的取捨問題也需要特別注意。多邊形的邊界與掃描線會產生兩個交點,填充時如果對兩個交點以及之間的區域都填充,容易造成填充範圍擴大,影響最終光柵圖形化顯示的填充效果。為此,人們提出了“左閉右開”的原則,簡單解釋就是,如果掃描線交點是1和9,則實際填充的區間是[1,9),即不包括x座標是9的那個點。

2.2掃描線演算法實現

        掃描線演算法的整個過程都是圍繞“活動邊表(AET)”展開的,為了正確初始化“活動邊表”,需要初始化每條掃描線的“新邊表(NET)”,首先定義“新邊表”的資料結構。定義“新邊表”為一個數組,陣列的每個元素存放對應掃描線的所有“新邊”。因此定義“新邊表”如下:

510     std::vector< std::list<EDGE> > slNet(ymax - ymin + 1);

ymax和ymin是多邊形所有頂點中y座標的最大值和最小值,用於界定掃描線的範圍。slNet 中的第一個元素對應的是ymin所在的掃描線,以此類推,最後一個元素是ymax所在的掃描線。在開始對每條掃描線處理之前,需要先計算出多邊形的ymax和ymin並初始化“新邊表”:

503 void ScanLinePolygonFill(const Polygon& py, int color)

504 {

505     assert(py.IsValid());

506 

507     int ymin = 0;

508     int ymax = 0;

509     GetPolygonMinMax(py, ymin, ymax);

510     std::vector< std::list<EDGE> > slNet(ymax - ymin + 1);

511     InitScanLineNewEdgeTable(slNet, py, ymin, ymax);

512     //PrintNewEdgeTable(slNet);

513     HorizonEdgeFill(py, color); //水平邊直接畫線填充

514     ProcessScanLineFill(slNet, ymin, ymax, color);

515 }

        InitScanLineNewEdgeTable()函式根據多邊形的頂點和邊的情況初始化“新邊表”,實現過程中體現了對左頂點和右頂點的區間修正原則:

315 void InitScanLineNewEdgeTable(std::vector< std::list<EDGE> >& slNet,

316                               const Polygon& py, int ymin, int ymax)

317 {

318     EDGE e;

319     for(int i = 0; i < py.GetPolyCount(); i++)

320     {

321         const Point& ps = py.pts[i];

322         const Point& pe = py.pts[(+ 1) % py.GetPolyCount()];

323         const Point& pss = py.pts[(- 1 + py.GetPolyCount()) %py.GetPolyCount()];

324         const Point& pee = py.pts[(+ 2) % py.GetPolyCount()];

325 

332         if(pe.!= ps.y) //不處理水平線

333         {

334             e.dx = double(pe.- ps.x) / double(pe.- ps.y);

335             if(pe.> ps.y)

336             {

337                 e.xi = ps.x;

338                 if(pee.>= pe.y)

339                     e.ymax = pe.- 1;

340                 else

341                     e.ymax = pe.y;

342 

343                 slNet[ps.- ymin].push_front(e);

344             }

345             else

346             {

347                 e.xi = pe.x;

348                 if(pss.>= ps.y)

349                     e.ymax = ps.- 1;

350                 else

351                     e.ymax = ps.y;

352                 slNet[pe.- ymin].push_front(e);

353             }

354         }

355     }

356 }

多邊形的定義Polygon和本系列第一篇《計算幾何與圖形學有關的幾種常用演算法》一文中的定義一致,此處就不再重複說明。演算法通過遍歷所有的頂點獲得邊的資訊,然後根據與此邊有關的前後兩個頂點的情況確定此邊的ymax是否需要-1修正。ps和pe分別是當前處理邊的起點和終點,pss是起點的前一個相鄰點,pee是終點的後一個相鄰點,pss和pee用於輔助判斷ps和pe兩個點是否是左頂點或右頂點,然後根據判斷結果對此邊的ymax進行-1修正,演算法實現非常簡單,注意與掃描線平行的邊是不處理的,因為水平邊直接在HorizonEdgeFill()函式中填充了。

         ProcessScanLineFill()函式開始對每條掃描線進行處理,對每條掃描線的處理有四個操作,如下程式碼所示,四個操作分別被封裝到四個函式中:

467 void ProcessScanLineFill(std::vector< std::list<EDGE> >& slNet,

468                          int ymin, int ymax, int color)

469 {

470     std::list<EDGE> aet;

471 

472     for(int y = ymin; y <= ymax; y++)

473     {

474         InsertNetListToAet(slNet[- ymin], aet);

475         FillAetScanLine(aet, y, color);

476         //刪除非活動邊

477         RemoveNonActiveEdgeFromAet(aet, y);

478         //更新活動邊表中每項的xi值,並根據xi重新排序

479         UpdateAndResortAet(aet);

480     }

481 }

InsertNetListToAet()函式負責將掃描線對應的所有新邊插入到aet中,插入操作到保證aet還是有序表,應用了插入排序的思想,實現簡單,此處不多解釋。FillAetScanLine()函式執行具體的填充動作,它將aet中的邊交點成對取出組成填充區間,然後根據“左閉右開”的原則對每個區間填充,實現也很簡單,此處不多解釋。RemoveNonActiveEdgeFromAet()函式負責將對下一條掃描線來說已經不是“活動邊”的邊從aet中刪除,刪除的條件就是當前掃描線y與邊的ymax相等,如果有多條邊滿足這個條件,則一併全部刪除:

439 bool IsEdgeOutOfActive(EDGE e, int y)

440 {

441     return (e.ymax == y);

442 }

443 

444 void RemoveNonActiveEdgeFromAet(std::list<EDGE>& aet, int y)

445 {

446     aet.remove_if(std::bind2nd(std::ptr_fun(IsEdgeOutOfActive), y));

447 }

UpdateAndResortAet()函式更新邊表中每項的xi值,就是根據掃描線的連貫性用dx對其進行修正,並且根據xi從小到大的原則對更新後的aet表重新排序:

449 void UpdateAetEdgeInfo(EDGE& e)

450 {

451     e.xi += e.dx;

452 }

453 

454 bool EdgeXiComparator(EDGE& e1, EDGE& e2)

455 {

456     return (e1.xi <= e2.xi);

457 }

458 

459 void UpdateAndResortAet(std::list<EDGE>& aet)

460 {

461     //更新xi

462     for_each(aet.begin(), aet.end(), UpdateAetEdgeInfo);

463     //根據xi從小到大重新排序

464     aet.sort(EdgeXiComparator);

465 }

        其實更新完xi後對aet表的重新排序是可以避免的,只要在維護aet時,除了保證xi從小到大的排序外,在xi相同的情況下如果能保證修正量dx也是從小到大有序,就可以避免每次對aet進行重新排序。演算法實現也很簡單,只需要對InsertNetListToAet()函式稍作修改即可,有興趣的朋友可以自行修改。

        至此,掃描線演算法就介紹完了,演算法的思想看似複雜,實際上並不難,從具體演算法的實現就可以看出來,整個演算法實現不足百行程式碼。

#include<windows.h>
#include<GL/glut.h>
const int POINTNUM=7;      //多邊形點數.

/******定義結構體用於活性邊表AET和新邊表NET***********************************/
typedef struct XET
{
    float x;
    float dx,ymax;
    XET* next;
} AET,NET;

/******定義點結構體point******************************************************/
struct point
{
    float x;
    float y;
} polypoint[POINTNUM]= { {250,50},{550,150},{550,400},{250,250},{100,350},{100,100},{120,30} }; //多邊形頂點

void PolyScan()
{
    /******計算最高點的y座標(掃描到此結束)****************************************/
    int MaxY=0;
    int i;
    for(i=0; i<POINTNUM; i++)
        if(polypoint[i].y>MaxY)
            MaxY=polypoint[i].y;

    /*******初始化AET表***********************************************************/
    AET *pAET=new AET;
    pAET->next=NULL;

    /******初始化NET表************************************************************/
    NET *pNET[1024];
    for(i=0; i<=MaxY; i++)
    {
        pNET[i]=new NET;
        pNET[i]->next=NULL;
    }
    glClear(GL_COLOR_BUFFER_BIT);        //賦值的視窗顯示.
    glColor3f(0.0,0.0,0.0);             //設定直線的顏色紅色
    glBegin(GL_POINTS);
    /******掃描並建立NET表*********************************************************/
    for(i=0; i<=MaxY; i++)
    {
        for(int j=0; j<POINTNUM; j++)
            if(polypoint[j].y==i)
            {
                //一個點跟前面的一個點形成一條線段,跟後面的點也形成線段
                if(polypoint[(j-1+POINTNUM)%POINTNUM].y>polypoint[j].y)
                {
                    NET *p=new NET;
                    p->x=polypoint[j].x;
                    p->ymax=polypoint[(j-1+POINTNUM)%POINTNUM].y;
                    p->dx=(polypoint[(j-1+POINTNUM)%POINTNUM].x-polypoint[j].x)/(polypoint[(j-1+POINTNUM)%POINTNUM].y-polypoint[j].y);
                    p->next=pNET[i]->next;
                    pNET[i]->next=p;
                }
                if(polypoint[(j+1+POINTNUM)%POINTNUM].y>polypoint[j].y)
                {
                    NET *p=new NET;
                    p->x=polypoint[j].x;
                    p->ymax=polypoint[(j+1+POINTNUM)%POINTNUM].y;
                    p->dx=(polypoint[(j+1+POINTNUM)%POINTNUM].x-polypoint[j].x)/(polypoint[(j+1+POINTNUM)%POINTNUM].y-polypoint[j].y);
                    p->next=pNET[i]->next;
                    pNET[i]->next=p;
                }
            }
    }
    /******建立並更新活性邊表AET*****************************************************/
    for(i=0; i<=MaxY; i++)
    {
        //計算新的交點x,更新AET
        NET *p=pAET->next;
        while(p)
        {
            p->x=p->x + p->dx;
            p=p->next;
        }
        //更新後新AET先排序*************************************************************/
        //斷表排序,不再開闢空間
        AET *tq=pAET;
        p=pAET->next;
        tq->next=NULL;
        while(p)
        {
            while(tq->next && p->x >= tq->next->x)
                tq=tq->next;
            NET *s=p->next;
            p->next=tq->next;
            tq->next=p;
            p=s;
            tq=pAET;
        }
        //(改進演算法)先從AET表中刪除ymax==i的結點****************************************/
        AET *q=pAET;
        p=q->next;
        while(p)
        {
            if(p->ymax==i)
            {
                q->next=p->next;
                delete p;
                p=q->next;
            }
            else
            {
                q=q->next;
                p=q->next;
            }
        }
        //將NET中的新點加入AET,並用插入法按X值遞增排序**********************************/
        p=pNET[i]->next;
        q=pAET;
        while(p)
        {
            while(q->next && p->x >= q->next->x)
                q=q->next;
            NET *s=p->next;
            p->next=q->next;
            q->next=p;
            p=s;
            q=pAET;
        }
        /******配對填充顏色***************************************************************/

        p=pAET->next;
        while(p && p->next)
        {
            for(float j=p->x; j<=p->next->x; j++)
                glVertex2i(static_cast<int>(j),i);
            p=p->next->next;//考慮端點情況
        }
    }
    glEnd();
    glFlush();
}

void init(int argc,char** argv)
{
    glutInit(&argc,argv);  //I初始化 GLUT.
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);  //設定顯示模式:單個快取和使用RGB模型
    glutInitWindowPosition(50,100);  //設定視窗的頂部和左邊位置
    glutInitWindowSize(400,300);  //設定視窗的高度和寬度
    glutCreateWindow("Scan Program");

    glClearColor(1.0,1.0,1.0,0); //視窗背景顏色為白色
    glMatrixMode(GL_PROJECTION);
    gluOrtho2D(0,600,0,450);
}

void myDisplay(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(0.0,0.4,0.2);
    glPointSize(1);
    glBegin(GL_POINTS);
    PolyScan();
    glEnd();
    glFlush();
}

int main(int argc,char** argv)
{
    init(argc,argv);
    glutDisplayFunc(myDisplay);        //圖形的定義傳遞給我window.
    glutMainLoop();
    return 0;
}


相關推薦

多邊形區域填充演算法掃描線填充演算法有序

二、掃描線演算法(Scan-Line Filling)         掃描線演算法適合對向量圖形進行區域填充,只需要直到多邊形區域的幾何位置,不需要指定種子點,適合計算機自動進行圖形處理的場合使用,比如電腦遊戲和三維CAD軟體的渲染等等。         對向量多

系列之十二:多邊形區域填充演算法掃描線填充演算法有序

、掃描線演算法(Scan-Line Filling)         掃描線演算法適合對向量圖形進行區域填充,只需要直到多邊形區域的幾何位置,不需要指定種子點,適合計算機自動進行圖形處理的場合使用,比如電腦遊戲和三維CAD軟體的渲染等等。         對向量多邊形區域

多邊形填充演算法-有序掃描線演算法 計算機圖形學

1.演算法的基本思想(掃描線連貫性原理):   對於一個給定的多邊形,用一組水平(垂直)的掃描線進行掃描,對每一條掃描線均可求出與多邊形邊的交點,這些交點將掃描線分割成落在多邊形內部的線段和落在多邊形外部的線段;並且二者相間排列。於是,將落在多邊形內部的線段上的所有象素點賦以給定的色彩值。

論文:基於粒子群優化的測試資料生成及其實證分析生成過程以及實驗計算機研究與發展

來源:2012年版的計算機研究與發展期刊 基於PSO的測試資料生成 (1) 核心問題:  如何保證PSO搜尋演算法和測試過程的協作執行  演算法的基本的流程: (1)對被測程式P進行靜態分析並完成: 1.  提取程式的 介面資

6LUA程式設計編譯執行與錯誤compile 、run & error處理

1.編譯問題 ---------------------------- ---------------------------- 首先我們談一下編譯的問題,LUA的執行,是將原始碼轉換成中間程式碼的形式執行的。 說到這裡,也許會有不少人會問,LUA不是一種解釋型語言,沒錯!

LeetCode演算法之TwoSum雜湊 簡單

首先先給出問題描述, 給定一個整數陣列,返回兩個數字的索引,使它們相加到特定目標。 您可以假設每個輸入只有一個解決方案,並且您可能不會兩次使用相同的元素。 例: 給定nums = [2,7,11,15],target = 9, 因為nums [ 0 ] + nums [ 1 ] =

[GIS演算法] DEM相關處理 - 視窗分析以平均值為例| 裁剪 - C語言實現

#include<stdio.h> #include<stdlib.h> typedef struct{ double x0,y0; //左下角點的座標值 int dx,dy; //柵格單元大小 int ycount,xcount; //行列號 doub

poj 3525 Most Distant Point from the Sea內縮半平面交求多邊形中可以放入最大的圓的半徑

題目連結:http://poj.org/problem?id=3525 題意:在凸邊形內找出一點,使得到多邊形邊界的距離最大。 題解: 參考部落格:https://blog.csdn.net/zuzhiang/article/details/78404556 轉化為求多邊形內可以

排序演算法之——優先佇列經典實現基於二叉堆

許多應用都需要處理有序的元素,但有時,我們不要求所有元素都有序,或是一定要一次就將它們排序,許多情況下,我們會收集這些元素裡的最大值或最小值。 這種情況下一個合適的資料結構應該支援兩種操作:插入元素、刪除最大元素。 優先佇列與棧和佇列類似,但它有自己的奇妙之處。 在本文中,會講解基於二叉堆的一種優先佇列

演算法筆記 第四章 排序選擇排序 插入排序

選擇排序: #include<iostream> using namespace std; void selectsort(int a[],int n){//陣列傳的就是首地址 for(int i=0;i<n-1;i++){ int min=i;

關於資料結構演算法中的比較排序(一)附Java程式碼實現

     現在已經是10月份,秋招正在進行,不知道是不是有的人會和我一樣正在瘋狂的複習起資料結構,在這裡我將就常見的幾種比較排序做一些簡單的解析,同時附上具體的程式碼實現。 1.氣泡排序 氣泡排序通常是我們最先接觸道的比較排序的一種,具體排序步驟如下: 1.比較相鄰的元

C/C++ 演算法分析與設計:貪心今年暑假不AC

題目描述 “今年暑假不AC?” “是的。” “那你幹什麼呢?” “看世界盃呀,笨蛋!” “@#$%^&*%...” 確實如此,世界盃來了,球迷的節日也來了,估計很多ACMer也會拋開電腦,奔向電視了。 作為球迷,一定想看盡量多的完整的比賽,當然,作為新時代的好青年,

簡單貪心演算法——字典序最小問題Best Cow Line POJ3617

題目連線 Best Cow Line POJ3617 題目描述 FJ is about to take his N (1 ≤ N ≤ 2,000) cows to the annual"Farmer of the Year" competition. In this

遺傳演算法解決TSP旅行商問題附:Python實現

前言 我先囉嗦一下:不是很喜歡寫計算智慧的演算法,因為一個演算法就要寫好久。前前後後將近有兩天的時間。 好啦,現在進入正題。 巡迴旅行商問題(TSP)是一個組合優化方面的問題,已經成為測試組合優化新演算法的標準問題。應用遺傳演算法解決 TSP 問題,首先對訪問

Floyd演算法求單源最短路圖,資料結構

Floyd演算法思路:計算某點到其餘各點的距離,可先求該點到其中一個點的距離,其他各點類似。假設求i點到j點的距離,跳點為空時,最短距離就是i到j的最短距離,跳點為1時,最短距離為D[i][j] = min{D[i][j],D[i][1]+D[1][j]},跳點為1和2時,最短距離為D[i][j]=min{D

演算法分析——排序演算法歸併排序複雜度分析遞迴樹

前面對演算法分析的一些常用的 漸進符號 做了簡單的描述,這裡將使用歸併排序演算法做為一個實戰演練。 這裡首先假設讀者對歸併排序已經有了簡單的瞭解(如果不瞭解的同學可以自行百度下歸併排序的原理)。瞭解此演算法的同學應都知道,歸併排序的主要思想是分而治之(簡稱分治)。分治演算法

藍橋杯 演算法訓練 校門外的樹線段樹+懶惰標記

演算法訓練 校門外的樹   時間限制:1.0s   記憶體限制:256.0MB 問題描述   某校大門外長度為L的馬路上有一排樹,每兩棵相鄰的樹之間的間隔都是1米。我們可以把馬路看成

梯度下降演算法原理與反向傳播思想推導及核心觀點

梯度下降方法是常用的引數優化方法,經常被用在神經網路中的引數更新過程中。 神經網路中,將樣本中的輸入X和輸出Y當做已知值(對於一個樣本[X,Y],其中X和Y分別是標準的輸入值和輸出值,X輸入到模型中計算得到Y,但是模型中的引數值我們並不知道,所以我們的做法是隨機初始化模型的

設計一個演算法,輸出從u到v的所有最短路徑採用鄰接儲存

思想:用path陣列存放路徑(初始為空),d表示路徑長度(初始為-1),查詢從頂點u到v的最短路徑過程如圖所示: 對應演算法如下: void FindPath(

演算法學習之排序演算法直接插入排序

1、插入法排序原理 直接插入排序(Insertion Sort)的基本思想是:每次將一個待排序的記錄,按其關鍵字大小插入到前面已經排好序的子序列中的適當位置,直到全部記錄插入完成為止。 設陣列為a[0…n-1]。 1. 初始時,a[0]自成1