二分匹配

发布于 2024-07-21 00:41:22 字数 328 浏览 4 评论 0原文

如何在 C 或 C++ 中实现二分匹配算法(可能基于最大流算法)?

具体来说,我在文件中输入了以下内容: (1,3) (1,5) (2,5)

(M,F) --> 其中M代表MALE的id,F代表FEMALE的id。

我需要找到最大匹配数并显示匹配的情侣。 喜欢: 匹配: 1&3 , 2&5

我在一些书中读过,我可以将这个问题基于“网络中的最大流量”算法,但除了“这个问题可以解决”这句话之外,我找不到任何具体信息通过......算法”。 我对 max-flow 知之甚少,也不知道如何实现它......

How can I implement a bipartite matching algorithm (probably based on a max-flow algorithm) in C or C++ ?

To be specific, I have this input in a file:
(1,3)
(1,5)
(2,5)

(M,F) --> where M represents id of MALE and F is id of FEMALE.

I need to find the maximum number of matches and show matched couples.
Like:
matches: 1&3 , 2&5

I have read in some books I can base this problem on a "maximum flow in a network" algorithm, but I couldn't find any specific info other than the sentence "this problem can be solved by .... algorithm".
I have little knowledge about max-flow, and dont know how to implement it either...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

夏花。依旧 2024-07-28 00:41:28
#include<stdio.h> 
#include<conio.h> 


void main() 
{ 
    int m,n,x,y,i,j,i1,j1,maxvalue;
    float s[10][10] = {0,0};
    int s2[10][10] = {0,0};
    float f[20][20] = {0,0};
    float f1[20][20] = {0,0};
    float f2[20][20] = {0,0};

    printf("\nEnter Number of Jobs(rows) and Machines(columns) of Matrix:\n"); 
    scanf_s("%d%d",&m,&n); 
    printf("\nEnter the Pij elements of matrix:\n"); 
    for(x=1;x<m+1;++x)
        for(y=1;y<n+1;++y)
            scanf("%f", &s[x][y]);
    //Find sum of each row
    for(x=1;x<m+1;++x)
    {
        s[x][n+1]=0;
        for(y=1;y<n+1;++y)




s[x][n+1]=s[x][n+1]+s[x][y];

//Find sum of each column
    for(y=1;y<n+1;++y)
    {
        s[m+1][y]=0;
        for(x=1;x<m+1;++x)
            s[m+1][y]+=s[x][y];
    }
    printf("\nMatrix s, Row Sum (Last Column)   and Column Sum (Last Row) : \n");

    printf("\ns:\n");
    for(x=1;x<m+2;++x)
    {
        for(y=1;y<n+2;++y)
            printf(" %2.0f  " , s[x][y]);
        printf("\n");
    }
    //Print sum of each column
    /*x=n+1;
    for(y=1;y<m+1;++y)
    printf(" %2.0f  " , s[x][y]);*/
    printf("\n");

    maxvalue = s[1][1];
    for(x=1; x<m+2; ++x)
        for(y=1; y<n+2; ++y)
        {




if(maxvalue < s[x+1][y+1])
                maxvalue = s[x+1][y+1];
        }



        printf("\n");
        printf("maxvalue = %d" , maxvalue);

        printf("\nd1:\n");
        float d1[20][20] = {0,0};
        for(i=1;i<=m;++i)
        {
            for(j=1;j<=m;++j)
            {
                if(i==j)
                    d1[i][j] = maxvalue - s[i][n+1];
                printf(" %2.0f  " , d1[i][j]);
            }
            printf("\n");
        }

        printf("\nd2\n");
        float d2[20][20] = {0,0};
        for(i=1;i<=n;++i)
        {
            for(j=1;j<=n;++j)
            {
                if(i==j)
                    d2[i][j] = maxvalue - s[m+1][j];
                printf(" %2.0f  " , d2[i][j]);



            }
            printf("\n");
        }



//row diff:
        printf("\n\nRow diff:\n");
        float r[20]= {0};
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
            {
                if(i == j)
                {
                    r[i] = maxvalue - d2[i][j];
                    printf("%f ",r[i]);
                }
            }

            //col diff:
            printf("\n\nCol diff:\n");
            float c[20]= {0};
            for(i=1;i<=m;i++)
                for(j=1;j<=m;j++)
                {
                    if(i == j)
                    {
                        c[i] = maxvalue - d1[i][j];
                        printf("%f ",c[i]);
                    }




                }

                //assignment matrix:
                float am[20][20]={0}; 




                i=j=1; 
ITERATION1: 
                if((c[i]<r[j]) && i<=m && j<=n) 
                { 
                    am[j][i]=c[i]; 
                    r[j]=r[j]-c[i]; 
                    c[i]=0; 
                    i++; 
                } 
                else if((c[i]>r[j]) && i<=m && j<=n) 
                { 
                    am[j][i]=r[j]; 
                    c[i]=c[i]-r[j]; 
                    r[j]=0; 
                    j++; 
                } 
                else if((c[i]==r[j]) && i<=m && j<=n) 
                { 
                    am[j][i]=r[j]; 
                    c[i]=r[j]=0; 
                    i++;j++; 
                } 
                else 



                    goto END; 
                for(int z=0;z<=n;z++) 
                { 
                    if(c[z]==0) 
                        continue; 


                    else 
                        goto ITERATION1; 
                } 

                for(int b=0;b<=m;b++) 
                { 
                    if(r[b]==0) 
                        continue; 
                    else 
                        goto ITERATION1; 
                } 

END: 
                printf("\n\nASSIGNMENT MATRIX:\n"); 

                for(i=1;i<=n;i++) 
                { 
                    for(j=1;j<=m;j++) 
                    { 
                        printf(" %2.0f  ",am[i][j]); 
                    } 
                    printf("\n"); 
                } 




                printf("\n\nf:\n");
                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)
                    {
                        if((i<=m) && (j<=n))


                    {
                            f[i][j]=s[i][j];
                        }
                        if((i<=m)&&(j>n))
                        {
                            f[i][j] = d1[i][j-n];
                        }
                        if((i>m)&&(j<=n))
                        {
                            f[i][j] = d2[i-m][j];
                        }
                        if((i>m)&&(j>n))
                        {
                            f[i][j] = am[i-m][j-n];
                        }

                        printf(" %2.0f  " , f[i][j]);
                    }
                    printf("\n");
                }




                //printf("\n\nf1:\n");
                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)
                    {
                        f1[i][j]=f[i][j];

                        //printf(" %2.0f  " , f1[i][j]);


                }
                    //printf("\n");
                }


                int cnt = 0;
ITERATION2: 
                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)
                    {
                        f2[i][j] = -1;
                    }
                }

                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)
                    {



                        if(f1[i][j]!=0 && f2[i][j]!=0)
                        {
                            f2[i][j] = f1[i][j];
                            for(j1=j+1;j1<(m+n)+1;++j1)
                                f2[i][j1] = 0;
                            for(i1=i+1;i1<(m+n)+1;++i1)
                                f2[i1][j] = 0;
                        }
                    }
                }



                //printf("\n\nf2:\n");
                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)
                    {
                        if(f2[i][j] == -1)
                        {
                            f2[i][j] = 0;
                        }
                        //printf(" %2.0f  " , f2[i][j]);
                    }
                    //printf("\n");
                }

                //printf("\n\nf1:\n");
                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)



                    {
                        if(f2[i][j] != 0)
                        {
                            f1[i][j] = f1[i][j] - 1;
                        }
                        //printf(" %2.0f  " , f1[i][j]);
                    }
                    //printf("\n");
                }

                cnt++;


                printf("\nITERATION - %d", cnt);
                printf("\n\Gant Chart:\n");
                for(i=1; i<=m;++i)
                {
                    for(j=1;j<=n;++j)
                    {
                        if(f2[i][j] != 0)
                        {
                            s2[i][cnt] = j;
                            printf(" J%d -> M%d", i,j);
                        }
                    }
                    printf("\n");
                }

                int sum = 1;
                for(i=1; i<(m+n)+1;++i)
                {



                    for(j=1;j<(m+n)+1;++j)
                    {   
                        sum = sum + f1[i][j];
                    }
                }

                if(sum>1)
                    goto ITERATION2;
                else
                    goto END2;
END2:



                printf("\n\Final Gant Chart:\n");
                for(i=1; i<=m;++i)
                {
                    for(j=0;j<=cnt;++j)
                    {
                        if(j == 0 )
                            printf(" J%d -> ", i);
                        else
                        {
                            if(s2[i][j] !=0)
                                printf(" M%d ", s2[i][j]);
                            else
                                printf(" %2c ", ' ');
                        }
                    }
                    printf("\n");
                }



                getch();
}
#include<stdio.h> 
#include<conio.h> 


void main() 
{ 
    int m,n,x,y,i,j,i1,j1,maxvalue;
    float s[10][10] = {0,0};
    int s2[10][10] = {0,0};
    float f[20][20] = {0,0};
    float f1[20][20] = {0,0};
    float f2[20][20] = {0,0};

    printf("\nEnter Number of Jobs(rows) and Machines(columns) of Matrix:\n"); 
    scanf_s("%d%d",&m,&n); 
    printf("\nEnter the Pij elements of matrix:\n"); 
    for(x=1;x<m+1;++x)
        for(y=1;y<n+1;++y)
            scanf("%f", &s[x][y]);
    //Find sum of each row
    for(x=1;x<m+1;++x)
    {
        s[x][n+1]=0;
        for(y=1;y<n+1;++y)




s[x][n+1]=s[x][n+1]+s[x][y];

//Find sum of each column
    for(y=1;y<n+1;++y)
    {
        s[m+1][y]=0;
        for(x=1;x<m+1;++x)
            s[m+1][y]+=s[x][y];
    }
    printf("\nMatrix s, Row Sum (Last Column)   and Column Sum (Last Row) : \n");

    printf("\ns:\n");
    for(x=1;x<m+2;++x)
    {
        for(y=1;y<n+2;++y)
            printf(" %2.0f  " , s[x][y]);
        printf("\n");
    }
    //Print sum of each column
    /*x=n+1;
    for(y=1;y<m+1;++y)
    printf(" %2.0f  " , s[x][y]);*/
    printf("\n");

    maxvalue = s[1][1];
    for(x=1; x<m+2; ++x)
        for(y=1; y<n+2; ++y)
        {




if(maxvalue < s[x+1][y+1])
                maxvalue = s[x+1][y+1];
        }



        printf("\n");
        printf("maxvalue = %d" , maxvalue);

        printf("\nd1:\n");
        float d1[20][20] = {0,0};
        for(i=1;i<=m;++i)
        {
            for(j=1;j<=m;++j)
            {
                if(i==j)
                    d1[i][j] = maxvalue - s[i][n+1];
                printf(" %2.0f  " , d1[i][j]);
            }
            printf("\n");
        }

        printf("\nd2\n");
        float d2[20][20] = {0,0};
        for(i=1;i<=n;++i)
        {
            for(j=1;j<=n;++j)
            {
                if(i==j)
                    d2[i][j] = maxvalue - s[m+1][j];
                printf(" %2.0f  " , d2[i][j]);



            }
            printf("\n");
        }



//row diff:
        printf("\n\nRow diff:\n");
        float r[20]= {0};
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
            {
                if(i == j)
                {
                    r[i] = maxvalue - d2[i][j];
                    printf("%f ",r[i]);
                }
            }

            //col diff:
            printf("\n\nCol diff:\n");
            float c[20]= {0};
            for(i=1;i<=m;i++)
                for(j=1;j<=m;j++)
                {
                    if(i == j)
                    {
                        c[i] = maxvalue - d1[i][j];
                        printf("%f ",c[i]);
                    }




                }

                //assignment matrix:
                float am[20][20]={0}; 




                i=j=1; 
ITERATION1: 
                if((c[i]<r[j]) && i<=m && j<=n) 
                { 
                    am[j][i]=c[i]; 
                    r[j]=r[j]-c[i]; 
                    c[i]=0; 
                    i++; 
                } 
                else if((c[i]>r[j]) && i<=m && j<=n) 
                { 
                    am[j][i]=r[j]; 
                    c[i]=c[i]-r[j]; 
                    r[j]=0; 
                    j++; 
                } 
                else if((c[i]==r[j]) && i<=m && j<=n) 
                { 
                    am[j][i]=r[j]; 
                    c[i]=r[j]=0; 
                    i++;j++; 
                } 
                else 



                    goto END; 
                for(int z=0;z<=n;z++) 
                { 
                    if(c[z]==0) 
                        continue; 


                    else 
                        goto ITERATION1; 
                } 

                for(int b=0;b<=m;b++) 
                { 
                    if(r[b]==0) 
                        continue; 
                    else 
                        goto ITERATION1; 
                } 

END: 
                printf("\n\nASSIGNMENT MATRIX:\n"); 

                for(i=1;i<=n;i++) 
                { 
                    for(j=1;j<=m;j++) 
                    { 
                        printf(" %2.0f  ",am[i][j]); 
                    } 
                    printf("\n"); 
                } 




                printf("\n\nf:\n");
                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)
                    {
                        if((i<=m) && (j<=n))


                    {
                            f[i][j]=s[i][j];
                        }
                        if((i<=m)&&(j>n))
                        {
                            f[i][j] = d1[i][j-n];
                        }
                        if((i>m)&&(j<=n))
                        {
                            f[i][j] = d2[i-m][j];
                        }
                        if((i>m)&&(j>n))
                        {
                            f[i][j] = am[i-m][j-n];
                        }

                        printf(" %2.0f  " , f[i][j]);
                    }
                    printf("\n");
                }




                //printf("\n\nf1:\n");
                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)
                    {
                        f1[i][j]=f[i][j];

                        //printf(" %2.0f  " , f1[i][j]);


                }
                    //printf("\n");
                }


                int cnt = 0;
ITERATION2: 
                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)
                    {
                        f2[i][j] = -1;
                    }
                }

                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)
                    {



                        if(f1[i][j]!=0 && f2[i][j]!=0)
                        {
                            f2[i][j] = f1[i][j];
                            for(j1=j+1;j1<(m+n)+1;++j1)
                                f2[i][j1] = 0;
                            for(i1=i+1;i1<(m+n)+1;++i1)
                                f2[i1][j] = 0;
                        }
                    }
                }



                //printf("\n\nf2:\n");
                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)
                    {
                        if(f2[i][j] == -1)
                        {
                            f2[i][j] = 0;
                        }
                        //printf(" %2.0f  " , f2[i][j]);
                    }
                    //printf("\n");
                }

                //printf("\n\nf1:\n");
                for(i=1; i<(m+n)+1;++i)
                {
                    for(j=1;j<(m+n)+1;++j)



                    {
                        if(f2[i][j] != 0)
                        {
                            f1[i][j] = f1[i][j] - 1;
                        }
                        //printf(" %2.0f  " , f1[i][j]);
                    }
                    //printf("\n");
                }

                cnt++;


                printf("\nITERATION - %d", cnt);
                printf("\n\Gant Chart:\n");
                for(i=1; i<=m;++i)
                {
                    for(j=1;j<=n;++j)
                    {
                        if(f2[i][j] != 0)
                        {
                            s2[i][cnt] = j;
                            printf(" J%d -> M%d", i,j);
                        }
                    }
                    printf("\n");
                }

                int sum = 1;
                for(i=1; i<(m+n)+1;++i)
                {



                    for(j=1;j<(m+n)+1;++j)
                    {   
                        sum = sum + f1[i][j];
                    }
                }

                if(sum>1)
                    goto ITERATION2;
                else
                    goto END2;
END2:



                printf("\n\Final Gant Chart:\n");
                for(i=1; i<=m;++i)
                {
                    for(j=0;j<=cnt;++j)
                    {
                        if(j == 0 )
                            printf(" J%d -> ", i);
                        else
                        {
                            if(s2[i][j] !=0)
                                printf(" M%d ", s2[i][j]);
                            else
                                printf(" %2c ", ' ');
                        }
                    }
                    printf("\n");
                }



                getch();
}
橘虞初梦 2024-07-28 00:41:28

以下是最大二分匹配的流算法的实验研究:

@article{cherkassky98,
   author = {Boris V. Cherkassky and Andrew V. Goldberg and Paul Martin and Joao C. Setubal and Jorge Stolfi},
   title = {Augment or Push:  A Computational Study of Bipartite Matching and Unit Capacity Flow Algorithms},
   journal = {Journal of Experimental Algorithmics},
   volume = 3,
   number = 8,
   year = 1998
}

获胜者是推送重新标记算法,我相信该算法是来自 Andrew Goldberg 的“BIM”包的实现,您可以在此处下载:

http://www.avglab.com/andrew/soft.html

请注意,如果编写代码很重要正如杰西建议的那样,您可能想选择福特-福克森。 如果您这样做,我建议您使用广度优先搜索,而不是深度优先搜索,来查找增广路径(原因在上面的文章中解释)。

Here is an experimental study of flow algorithms for maximum bipartite matching:

@article{cherkassky98,
   author = {Boris V. Cherkassky and Andrew V. Goldberg and Paul Martin and Joao C. Setubal and Jorge Stolfi},
   title = {Augment or Push:  A Computational Study of Bipartite Matching and Unit Capacity Flow Algorithms},
   journal = {Journal of Experimental Algorithmics},
   volume = 3,
   number = 8,
   year = 1998
}

The winner was a push-relabel algorithm, which I believe was the implementation from Andrew Goldberg's "BIM" package, which you can download here:

http://www.avglab.com/andrew/soft.html

Mind you, if it's important that you code up the solution yourself, you might want to settle for Ford-Fulkerson, as Jesse suggested. If you do that, I recommend you use breadth-first search, not depth-first search, to find the augmenting path (for reasons explained in the article above).

梦断已成空 2024-07-28 00:41:27

QuickGraph 库包含一个二分匹配算法,我刚刚研究并检查了修复。 它封装了 Edmonds Karp 最大流算法。

到目前为止,该算法的唯一文档是我添加的单元测试。 如果有人想添加一个(希望更快的)实现,而不是简单地包装最大流算法,请与我联系。

The QuickGraph library includes a bipartite matching algorithm, which I just worked on and checked in a fix for. It wraps the Edmonds Karp maximum flow algorithm.

The only documentation for the algorithm so far is the unit tests I added. If anyone would like to add a (hopefully faster) implementation which does not simply wrap a maxflow algorithm, please contact me.

或十年 2024-07-28 00:41:26

是的,如果您已经有解决最大流问题的代码,您可以使用它通过转换图形来解决二分匹配,如 这个讲座,但如果您从头开始,这可能不是正确的方法。 如果您只想实现一些相当简单的代码来解决问题,但示例不会太大,那么最好使用简单的增强路径方法,如下所示 此处。 这为您提供了一种 O(|V||E|) 方法,该方法非常容易编码,并且适用于除非常大的图形之外的所有图形。 如果您想要具有更好的最坏情况性能的东西,您可以尝试 Hopcraft-Karp 算法,它一次找到多个增广路径,并且具有 O(sqrt(|V|)|E|) 运行时间限制,但维基百科上的文章指出:

多位作者曾表演过
两部分的实验比较
匹配算法。 他们的结果是
一般倾向于表明
Hopcroft-Karp 方法在以下方面的效果较差
理论上的实践:它是
比两者都更简单
广度优先和深度优先
寻找增强的策略
路径,并通过推送重新标记
技术。

无论如何,在尝试解决 Hopcraft-Karp 或维基百科文章参考文献中提到的推送相关技术之一之前,您绝对应该理解并能够实现简单的增强路径方法。

编辑:由于某种原因,上面的链接没有正确显示。 以下是有问题的网址:(http://oucsace.cs .ohiou.edu/~razvan/courses/cs404/lecture21.pdf), (http://www.maths.lse.ac.uk/Courses/MA314/matching.pdf),以及(http://en.wikipedia.org/wiki/Hopcroft–Karp_algorithm)。

Yes, if you already have code to solve the max-flow problem, you can use it to solve bipartite matching by transforming the graph as shown towards the end of this lecture, but that's probably not the right approach if you are starting from scratch. If you just want to implement some fairly simple code to solve the problem for examples that don't get too huge, you are better off using a simple augmenting path approach as outlined here. That gives you an O(|V||E|) approach that is pretty easy to code and adequate for all but very large graphs. If you want something with better worst-case performance, you could try the Hopcraft-Karp algorithm, which finds multiple augmenting paths at once and has a O(sqrt(|V|)|E|) run time bound, but the Wikipedia article on it notes that:

Several authors have performed
experimental comparisons of bipartite
matching algorithms. Their results in
general tend to show that the
Hopcroft–Karp method is not as good in
practice as it is in theory: it is
outperformed both by simpler
breadth-first and depth-first
strategies for finding augmenting
paths, and by push-relabel
techniques.

In any case, you should definitely understand and be able to implement a simple augmenting-path approach before trying to tackle either Hopcraft-Karp or one of the push-relable techniques mentioned in the references of the Wikipedia article.

Edit: For some reason, the links above aren't showing up correctly. Here are the URLs in question: (http://oucsace.cs.ohiou.edu/~razvan/courses/cs404/lecture21.pdf), (http://www.maths.lse.ac.uk/Courses/MA314/matching.pdf), and (http://en.wikipedia.org/wiki/Hopcroft–Karp_algorithm).

∝单色的世界 2024-07-28 00:41:25

是的,二分匹配可以减少到最大流量:

  1. 您将获得一组节点 MF。 如果您已经获得了从 M 中的节点 mF 中的节点 f 的有向边,请添加一条有向边在您的文件中配对 (m, f)

  2. 添加一个节点S,其有向边从SM中的每个节点(这是您的“超级源”节点)。

  3. 添加一个节点 T,该节点具有从 F 中的每个节点到 T 的有向边(这是您的“超级接收器”节点) )。

  4. 现在,您需要找到从 ST 的最大流量(所有边的权重均为 1)。

那么最大流量到底是多少呢? 从 ST是一组边,使得对于每个节点(S>T),其流入边的权重与其流出边的权重相同。 想象一下,您的图表是一系列管道,您将水从 S 处倒入系统,并在 T 处放出。 在其间的每个节点,流入的水量必须与流出的水量相同。

尝试让自己相信一个流程对应于您的原始集合的匹配。 (给定一个流量,如何获得匹配?给定一个匹配,如何获得流量?)

最后,要找到图中的最大流量,可以使用 福特-Fulkerson 算法。 上面的维基百科页面用伪代码对其进行了很好的描述。

Yes, bipartite matching can be reduced to maximum flow:

  1. You're given sets of nodes M and F. Add a directed edge from a node m in M to a node f in F if you've got the pair (m, f) in your file.

  2. Add a single node S with a directed edge from S to every node in M (this is your "super-source" node).

  3. Add a single node T with a directed edge from every node in F to T (this is your "super-sink" node).

  4. Now, you need to find the maximum flow (with all your edges of weight 1) from S to T.

So what the heck is maximum flow? A flow from S to T is a set of edges such that for each node (except S and T), the weight of its in-flux edges is the same as the weight of its out-flux edges. Imagine that your graph is a series of pipes, and you're pouring water in the system at S and letting it out at T. At every node in between, the amount of water going in has to be the same as the amount of water coming out.

Try to convince yourself that a flow corresponds to a matching of your original sets. (Given a flow, how to you get a matching? Given a matching, how to you get a flow?)

Finally, to find the maximum flow in a graph, you can use the Ford-Fulkerson algorithm. The above wikipedia page gives a good description of it, with pseudo-code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文