我尝试实施karger Minimum Cut算法(Karger wiki page)
到目前为止,我已经在小例子(大小为10的输入)上尝试了我的算法,它似乎有效。但是当我尝试获得更大的输入时,让我们说200.它只是崩溃。
为了存储最小切割数据,我创建了一个2D数组:GraphCut [SIZE_ARRAY] [SIZE_ARRAY_2] 在这种情况下,SIZE_ARRAY = 200,但我找不到SIZE_ARRAY_2的长度。
问题是,SIZE_ARRAY_2必须很大,因为我修改了初始数组以合并不同的顶点。
如果我声明SIZE_ARRAY_2 = 200,则大小不够,但如果我把SIZE_ARRAY_2 = 1000,程序就会崩溃。
问题是,我必须执行算法100000次。
以下是代码的一部分:
#define ARRAY_SIZE 200
#define ARRAY_SIZE_2 200
int main()
{
int minCut,minMinCut;
for (int k = 0; k < ARRAY_SIZE * ARRAY_SIZE * 4;k++) {
minCut = kargerMinCut(k);
if (k == 0)
minMinCut = minCut;
else if (minMinCut > minCut)
minMinCut = minCut;
}
printf("\n minMinCut = %d\n", minMinCut);
return 0;
}
int kargerMinCut(int k) {
// 1st dimension: each different node
// 2nd dimension: vertices
long graphCut[ARRAY_SIZE + 1][ARRAY_SIZE_2] = {0};
populateIntegerArray(graphCut); // import data from a file
int nodeToMain[ARRAY_SIZE + 1];
int sizeOfMainNode, indexToMerge,initialRand,i,j,m,nodeToMerge,nodeRemaining = ARRAY_SIZE;
for (m = 0;m<ARRAY_SIZE + 1;m++) // initialization of nodeToMain
nodeToMain[m] = m;
while (nodeRemaining > 2) {
i = 0;
j = 0;
srand(time(NULL) + nodeRemaining);// initialise rand
initialRand = nodeToMain[rand()%(ARRAY_SIZE) + 1]; // pick a random initial node, but not a merged one
sizeOfMainNode = sizeOfArray(graphCut[initialRand]); // size of the initial node
srand(time(NULL) + k); // initialise rand
indexToMerge = rand()%sizeOfMainNode;// pick another random node in the linked nodes (its index to be precise)
nodeToMerge = nodeToMain[graphCut[initialRand][indexToMerge]];
for (m = 0;m<ARRAY_SIZE + 1;m++) // update the nodeToMain array, initialRand is now the main node for nodeToMerge
if (nodeToMain[m] == nodeToMerge)
nodeToMain[m] = initialRand;
// remove the nodeToMerge numbers from the graphCut[initialRand] (as they are going to be merged)
while(graphCut[initialRand][j] > 0 && j < sizeOfMainNode) {
if (initialRand == nodeToMain[graphCut[initialRand][j]]) {
// if this is the last element, do nothing
while(nodeToMain[graphCut[initialRand][sizeOfMainNode - 1]] == initialRand && j < sizeOfMainNode - 1) {
graphCut[initialRand][sizeOfMainNode - 1] = 0;
sizeOfMainNode--;
}
graphCut[initialRand][j] = nodeToMain[graphCut[initialRand][sizeOfMainNode - 1]];
graphCut[initialRand][sizeOfMainNode - 1] = 0;
sizeOfMainNode--;
}
j++;
}
i = 0;
while (graphCut[nodeToMerge][i] > 0 && sizeOfMainNode < ARRAY_SIZE_2 && i < ARRAY_SIZE_2) { // add each vextex of the nodeTomerge to the merged nodes
if (nodeToMain[graphCut[nodeToMerge][i]] != initialRand) {
graphCut[initialRand][sizeOfMainNode] = nodeToMain[graphCut[nodeToMerge][i]];
sizeOfMainNode++;
}
i++;
}
nodeRemaining--;
}
return sizeOfArray(graphCut[nodeToMain[1]]);
}
我确信代码不是很干净,甚至可能非常糟糕(C语言中的初学者)。所以我欢迎任何其他建议。
我用调试器得到的错误似乎是随机的。 错误是:
无法除以0
它在time64.c第62行停止
tim = (__time64_t)((nt_time.ft_scalar - EPOCH_BIAS) / 10000000i64);
答案 0 :(得分:2)
数组大小的变化可能导致堆栈溢出。堆栈的常见默认大小为1MB(1048576字节)。如果你有:
long graphCut[200][1000];
和4 == sizeof(long)
graphCut
数组占用200 * 1000 * 4 = 800000
个字节,这会留下248576
个字节,这对于populateIntegerArray()
函数中的堆栈变量可能不够(我没看到那个功能)。如果8 == sizeof(long)
则数组将需要1600000
个字节,大于1MB。
如果需要该大小的数组,则在堆上而不是堆栈上分配(全部或部分)。例如:
long* graphCut[ARRAY_SIZE_1];
int i;
for (i = 0; i < sizeof(graphCut)/sizeof(graphCut[0]); i++)
{
graphCut[i] = malloc(ARRAY_SIZE_2 * sizeof(graphCut[0][0]));
memset(graphCut[i], 0, ARRAY_SIZE_2 * sizeof(graphCut[0][0]));
}
for (i = 0; i < sizeof(graphCut)/sizeof(graphCut[0]); i++)
{
free(graphCut[i]);
}
答案 1 :(得分:1)
一些可能的问题是整数或堆栈溢出(所以你在正确的站点上)和内存初始化。
这个实现应该在堆上分配graphCut,并在每次调用kargerMin时将其归零,从而解决这些问题。
int minCut, minMinCut;
// There is a small possibility that ARRAY_SIZE*ARRAY_SIZE*4 exceeds int boundary if 16-bit
long k;
long **buffer;
// Allocate graphCut on the heap
buffer = malloc((ARRAY_SIZE + 1)*sizeof(long *));
for (k = 0; k < ARRAY_SIZE + 1; k++)
buffer[k] = malloc(ARRAY_SIZE_2*sizeof(long));
for (k = 0; k < ARRAY_SIZE * ARRAY_SIZE * 4;k++) {
minCut = kargerMinCut(k, buffer);
if (k == 0)
minMinCut = minCut;
else if (minMinCut > minCut)
minMinCut = minCut;
}
printf("\n minMinCut = %d\n", minMinCut);
// Here we free the buffer. We could do it in any order, but
// as it costs nothing here to do so, we free it in reverse-
// allocation-order to avoid any possible memory fragmentation
// - which is moot anyway, if this is a main() and we're exiting
// the program. In other instances it could be relevant.
for (k = 0; k < ARRAY_SIZE + 1; k++)
{
free(buffer[ARRAY_SIZE-k]); buffer[ARRAY_SIZE-k] = NULL;
}
free(buffer); buffer = NULL;
// The NULLing of the just-freed variables has no purpose except
// to GUARANTEE that any illegal use of them, dangling pointers,
// leftover copies etc. will immediately trigger a core dump and
// be discovered, instead of lurking undetected.
return 0;
}
int kargerMinCut(long k, long **graphCut) {
// 1st dimension: each different node
// 2nd dimension: vertices
// Zero graphCut. If populateIntegerArray rewrites
// the whole of graphCut, these four lines are redundant.
int i, j;
for (i = 0; i < ARRAY_SIZE + 1; i++)
for (j = 0; j < ARRAY_SIZE_2; j++)
graphCut[i][j] = 0;
// otherwise, they make sure that no old value of graphCut
// or uninitialised value is going to linger and potentially
// corrupt calculations later on.
populateIntegerArray(graphCut); // import data from a file
答案 2 :(得分:0)
我在C ++中实现了Karger算法。我的下面的代码适用于大文件,但我还没有足够的优化...它仍然可以快速运行..但可以更快..试试这个解决方案。
#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <string>
#include <map>
#include <list>
#include <fstream>
#include <sstream>
#include <set>
#include <stdlib.h>
#include <time.h>
int pick_edge(std::map <int, std::list<int>> g2, set<int> myset, int &u, int &v)
{
std::map <int, std::list<int>>::iterator it;
std::list<int> eachRow;
int rand_vertex;
int rand_edge;
srand (time(NULL));
rand_vertex = (rand() + 1) % myset.size() ;
if (rand_vertex == 0)
rand_vertex = 1;
u = get_value_at_i(myset, rand_vertex);
for (it = g2.begin(); it != g2.end(); ++it) {
if (it->first == u) {
eachRow = it->second;
rand_edge = (rand() + 1) % eachRow.size();
if (rand_edge == 0)
rand_edge = 1;
v = get_edge_at_j(eachRow, rand_edge);
break;
}
}
return 0;
}
map <int, std::list<int>> merge_uv(map <int, std::list<int>> g2, int u, int v)
{
std::map <int, std::list<int>>::iterator it_g;
std::map <int, std::list<int>>::iterator it_u;
std::map <int, std::list<int>>::iterator it_v;
std::list<int>::iterator iter_l;
std::list<int> eachRow, uRow, vRow;
std::list<int> newRow;
int vertex;
int j = 0;
map <int, std::list<int>> new_Graph_G;
vRow.clear();
uRow.clear();
eachRow.clear();
newRow.clear();
for (it_g = g2.begin(); it_g != g2.end(); ++it_g) {
vertex = it_g->first;
eachRow = it_g->second;
if (vertex == u) {
uRow = it_g->second;
it_u = it_g;
j++;
continue;
}
if (vertex == v) {
vRow = it_g->second;
it_v = it_g;
j++;
continue;
}
}
if (j == 2) {
uRow.sort();
vRow.sort();
// uRow.merge(vRow);
for (std::list<int>::iterator ite = vRow.begin(); ite != vRow.end(); ++ite) {
if (*ite != u) {
uRow.push_back(*ite);
}
}
g2.erase(v);
g2[u] = uRow;
}
for (it_g = g2.begin(); it_g != g2.end(); ++it_g) {
eachRow = it_g->second;
for (std::list<int>::iterator ite = eachRow.begin(); ite != eachRow.end(); ++ite) {
if (*ite == v && *ite != it_g->first) {
newRow.push_back(u);
} else if (*ite == it_g->first) {
continue;
} else {
newRow.push_back(*ite);
}
}
new_Graph_G[it_g->first] = newRow;
newRow.clear();
}
for (it_g = g2.begin(); it_g != g2.end(); ++it_g) {
eachRow = it_g->second;
if (it_g->first == u) {
for (std::list<int>::iterator ite = eachRow.begin(); ite != eachRow.end(); ++ite) {
if (*ite != u && *ite != v) {
newRow.push_back(*ite);
}
}
new_Graph_G[it_g->first] = newRow;
break;
}
}
return new_Graph_G;
}
int get_min_cut(std::map <int, std::list<int>> g1)
{
int v;
std::list<int> eachRow;
std::map <int, std::list<int>>::iterator it_g;
int min_cut = 0;
for (it_g = g1.begin(); it_g != g1.end(); ++it_g) {
eachRow = it_g->second;
v = it_g->first;
for (std::list<int>::iterator ite = eachRow.begin(); ite != eachRow.end(); ++ite) {
if (*ite != v) {
min_cut++;
}
}
break;
}
return min_cut;
}
int EdgeContractionAlgorithm()
{
std::map <int, std::list<int>>::iterator it;
int min_cut = 0;
int vertex = 1;
std::list<int> eachRow;
std::set<int> myset;
std::set<int>::iterator itSet;
std::map <int, std::list<int>> g2;
int edge;
int n_vertices;
int cnt = 0;
int u, v;
n_vertices = Cal_nVertices(myset, Graph_G);
g2 = Graph_G;
// Contraction algorithm.
while (n_vertices > 2) {
edge = pick_edge(g2, myset, u, v);
g2 = merge_uv(Graph_G, u, v);
n_vertices = g2.size();
myset.erase (myset.find(v));
Graph_G = g2;
}
print_graph(g2);
min_cut = get_min_cut(g2);
return (min_cut);
}