Это дополнение к моему другому ответу , показывающее пример программы, которая рассчитывает кластеры белого (0) и черного (1) по отдельности. Это было слишком долго, чтобы уместиться в одном ответе.
Идея состоит в том, что мы используем отдельную карту для цвета каждой ячейки. Эта карта также содержит граничные ячейки с цветом, который не соответствует ни одному цвету в фактической матрице. Несвязанный набор представляет собой отдельный массив с одной записью на ячейку матрицы.
Чтобы отслеживать два цвета, мы используем отдельный массив для каждого цвета при подсчете количества вхождений на непересекающийся корневой набор. (Или, другими словами, размер каждого кластера в матрице.)
Когда статистика собирается, размеры кластеров обновляются до отдельной гистограммы для каждого цвета. Эта схема кажется довольно производительной, хотя потребление памяти является значительным. (Для матрицы из R
строк и C
столбцов требуется около 25*R*C + R + 2*C
(плюс константа) байтов, включая гистограммы.)
Вот заголовочный файл, clusters.h , который реализует всю вычислительную логику:
#ifndef CLUSTERS_H
#define CLUSTERS_H
#include <stdlib.h>
#include <inttypes.h>
#include <limits.h>
#include <time.h>
/* This file is in public domain. No guarantees, no warranties.
Written by Nominal Animal <question@nominal-animal.net>.
*/
/* For pure C89 compilers, use '-DSTATIC_INLINE=static' at compile time. */
#ifndef STATIC_INLINE
#define STATIC_INLINE static inline
#endif
#define ERR_INVALID -1 /* Invalid function parameter */
#define ERR_TOOLARGE -2 /* Matrix size is too large */
#define ERR_NOMEM -3 /* Out of memory */
typedef unsigned char cluster_color;
typedef uint32_t cluster_label;
typedef uint64_t cluster_count;
#define CLUSTER_WHITE 0
#define CLUSTER_BLACK 1
#define CLUSTER_NONE UCHAR_MAX /* Reserved */
#define FMT_COLOR "u"
#define FMT_LABEL PRIu32
#define FMT_COUNT PRIu64
typedef struct {
uint64_t state;
} prng;
typedef struct {
/* Pseudo-random number generator used */
prng rng;
/* Actual size of the matrix */
cluster_label rows;
cluster_label cols;
/* Number of matrices the histograms have been collected from */
cluster_count iterations;
/* Probability of each cell being black */
uint64_t p_black;
/* Probability of diagonal connections */
uint64_t d_black;
uint64_t d_white;
/* Cluster colormap contains (rows+2) rows and (cols+1) columns */
cluster_color *map;
/* Disjoint set of (rows) rows and (cols) columns */
cluster_label *djs;
/* Number of occurrences per disjoint set root */
cluster_label *white_roots;
cluster_label *black_roots;
/* Histograms of white and black clusters */
cluster_count *white_histogram;
cluster_count *black_histogram;
} cluster;
#define CLUSTER_INITIALIZER { {0}, 0, 0, 0, 0.0, 0.0, 0.0, NULL, NULL, NULL, NULL, NULL, NULL }
/* Calculate uint64_t limit corresponding to probability p. */
STATIC_INLINE uint64_t probability_limit(const double p)
{
if (p <= 0.0)
return UINT64_C(0);
else
if (p <= 0.5)
return (uint64_t)(p * 18446744073709551615.0);
else
if (p >= 1.0)
return UINT64_C(18446744073709551615);
else
return UINT64_C(18446744073709551615) - (uint64_t)((double)(1.0 - p) * 18446744073709551615.0);
}
/* Return true at probability corresponding to limit 'limit'.
This implements a Xorshift64* pseudo-random number generator. */
STATIC_INLINE int probability(prng *const rng, const uint64_t limit)
{
uint64_t state = rng->state;
uint64_t value;
/* To correctly cover the entire range, we ensure we never generate a zero. */
do {
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
value = state * UINT64_C(2685821657736338717);
} while (!value);
rng->state = state;
return (value <= limit) ? CLUSTER_BLACK : CLUSTER_WHITE;
}
/* Generate a random seed for the Xorshift64* pseudo-random number generator. */
static uint64_t randomize(prng *const rng)
{
unsigned int rounds = 127;
uint64_t state = UINT64_C(3069887672279) * (uint64_t)time(NULL)
^ UINT64_C(60498839) * (uint64_t)clock();
if (!state)
state = 1;
/* Churn the state a bit. */
while (rounds-->0) {
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
}
if (rng)
rng->state = state;
return state;
}
/* Free all resources related to a cluster. */
STATIC_INLINE void free_cluster(cluster *c)
{
if (c) {
/* Free dynamically allocated pointers. Note: free(NULL) is safe. */
free(c->map);
free(c->djs);
free(c->white_roots);
free(c->black_roots);
free(c->white_histogram);
free(c->black_histogram);
c->rng.state = 0;
c->rows = 0;
c->cols = 0;
c->iterations = 0;
c->p_black = 0;
c->d_white = 0;
c->d_black = 0;
c->map = NULL;
c->djs = NULL;
c->white_roots = 0;
c->black_roots = 0;
c->white_histogram = NULL;
c->black_histogram = NULL;
}
}
/* Initialize cluster structure, for a matrix of specified size. */
static int init_cluster(cluster *c, const int rows, const int cols,
const double p_black,
const double d_white, const double d_black)
{
const cluster_label label_cols = cols;
const cluster_label label_rows = rows;
const cluster_label color_rows = rows + 2;
const cluster_label color_cols = cols + 1;
const cluster_label color_cells = color_rows * color_cols;
const cluster_label label_cells = label_rows * label_cols;
const cluster_label labels = label_cells + 2; /* One extra! */
if (!c)
return ERR_INVALID;
c->rng.state = 0; /* Invalid seed for Xorshift64*. */
c->rows = 0;
c->cols = 0;
c->iterations = 0;
c->p_black = 0;
c->d_white = 0;
c->d_black = 0;
c->map = NULL;
c->djs = NULL;
c->white_roots = NULL;
c->black_roots = NULL;
c->white_histogram = NULL;
c->black_histogram = NULL;
if (rows < 1 || cols < 1)
return ERR_INVALID;
if ((unsigned int)color_rows <= (unsigned int)rows ||
(unsigned int)color_cols <= (unsigned int)cols ||
(cluster_label)(color_cells / color_rows) != color_cols ||
(cluster_label)(color_cells / color_cols) != color_rows ||
(cluster_label)(label_cells / label_rows) != label_cols ||
(cluster_label)(label_cells / label_cols) != label_rows)
return ERR_TOOLARGE;
c->black_histogram = calloc(labels, sizeof (cluster_count));
c->white_histogram = calloc(labels, sizeof (cluster_count));
c->black_roots = calloc(labels, sizeof (cluster_label));
c->white_roots = calloc(labels, sizeof (cluster_label));
c->djs = calloc(label_cells, sizeof (cluster_label));
c->map = calloc(color_cells, sizeof (cluster_color));
if (!c->map || !c->djs ||
!c->white_roots || !c->black_roots ||
!c->white_histogram || !c->black_histogram) {
free(c->map);
free(c->djs);
free(c->white_roots);
free(c->black_roots);
free(c->white_histogram);
free(c->black_histogram);
return ERR_NOMEM;
}
c->rows = rows;
c->cols = cols;
c->p_black = probability_limit(p_black);
c->d_white = probability_limit(d_white);
c->d_black = probability_limit(d_black);
/* Initialize the color map to NONE. */
{
cluster_color *ptr = c->map;
cluster_color *const end = c->map + color_cells;
while (ptr < end)
*(ptr++) = CLUSTER_NONE;
}
/* calloc() initialized the other arrays to zeros already. */
return 0;
}
/* Disjoint set: find root. */
STATIC_INLINE cluster_label djs_root(const cluster_label *const djs, cluster_label from)
{
while (from != djs[from])
from = djs[from];
return from;
}
/* Disjoint set: path compression. */
STATIC_INLINE void djs_path(cluster_label *const djs, cluster_label from, const cluster_label root)
{
while (from != root) {
const cluster_label temp = djs[from];
djs[from] = root;
from = temp;
}
}
/* Disjoint set: Flatten. Returns the root, and flattens the path to it. */
STATIC_INLINE cluster_label djs_flatten(cluster_label *const djs, cluster_label from)
{
const cluster_label root = djs_root(djs, from);
djs_path(djs, from, root);
return root;
}
/* Disjoint set: Join two subsets. */
STATIC_INLINE void djs_join2(cluster_label *const djs,
cluster_label from1, cluster_label from2)
{
cluster_label root, temp;
root = djs_root(djs, from1);
temp = djs_root(djs, from2);
if (root > temp)
temp = root;
djs_path(djs, from1, root);
djs_path(djs, from2, root);
}
/* Disjoint set: Join three subsets. */
STATIC_INLINE void djs_join3(cluster_label *const djs,
cluster_label from1, cluster_label from2,
cluster_label from3)
{
cluster_label root, temp;
root = djs_root(djs, from1);
temp = djs_root(djs, from2);
if (root > temp)
root = temp;
temp = djs_root(djs, from3);
if (root > temp)
root = temp;
djs_path(djs, from1, root);
djs_path(djs, from2, root);
djs_path(djs, from3, root);
}
/* Disjoint set: Join four subsets. */
STATIC_INLINE void djs_join4(cluster_label *const djs,
cluster_label from1, cluster_label from2,
cluster_label from3, cluster_label from4)
{
cluster_label root, temp;
root = djs_root(djs, from1);
temp = djs_root(djs, from2);
if (root > temp)
root = temp;
temp = djs_root(djs, from3);
if (root > temp)
root = temp;
temp = djs_root(djs, from4);
if (root > temp)
root = temp;
djs_path(djs, from1, root);
djs_path(djs, from2, root);
djs_path(djs, from3, root);
djs_path(djs, from4, root);
}
/* Disjoint set: Join five subsets. */
STATIC_INLINE void djs_join5(cluster_label *const djs,
cluster_label from1, cluster_label from2,
cluster_label from3, cluster_label from4,
cluster_label from5)
{
cluster_label root, temp;
root = djs_root(djs, from1);
temp = djs_root(djs, from2);
if (root > temp)
root = temp;
temp = djs_root(djs, from3);
if (root > temp)
root = temp;
temp = djs_root(djs, from4);
if (root > temp)
root = temp;
temp = djs_root(djs, from5);
if (root > temp)
root = temp;
djs_path(djs, from1, root);
djs_path(djs, from2, root);
djs_path(djs, from3, root);
djs_path(djs, from4, root);
djs_path(djs, from5, root);
}
static void iterate(cluster *const cl)
{
prng *const rng = &(cl->rng);
uint64_t const p_black = cl->p_black;
uint64_t d_color[2];
cluster_color *const map = cl->map + cl->cols + 2;
cluster_label const map_stride = cl->cols + 1;
cluster_label *const djs = cl->djs;
cluster_label *roots[2];
cluster_label const rows = cl->rows;
cluster_label const cols = cl->cols;
int r, c;
d_color[CLUSTER_WHITE] = cl->d_white;
d_color[CLUSTER_BLACK] = cl->d_black;
roots[CLUSTER_WHITE] = cl->white_roots;
roots[CLUSTER_BLACK] = cl->black_roots;
for (r = 0; r < rows; r++) {
cluster_label const curr_i = r * cols;
cluster_color *const curr_row = map + r * map_stride;
cluster_color *const prev_row = curr_row - map_stride;
for (c = 0; c < cols; c++) {
cluster_color color = probability(rng, p_black);
cluster_label label = curr_i + c;
uint64_t diag = d_color[color];
unsigned int joins = 0;
/* Assign the label and color of the current cell, */
djs[label] = label;
curr_row[c] = color;
/* Because we join left, up-left, up, and up-right, and
all those have been assigned to already, we can do
the necessary joins right now. */
/* Join left? */
joins |= (curr_row[c-1] == color) << 0;
/* Join up? */
joins |= (prev_row[c ] == color) << 1;
/* Join up left? */
joins |= (prev_row[c-1] == color && probability(rng, diag)) << 2;
/* Join up right? */
joins |= (prev_row[c+1] == color && probability(rng, diag)) << 3;
/* Do the corresponding joins. */
switch (joins) {
case 1: /* Left */
djs_join2(djs, label, label - 1);
break;
case 2: /* Up */
djs_join2(djs, label, label - cols);
break;
case 3: /* Left and up */
djs_join3(djs, label, label - 1, label - cols);
break;
case 4: /* Up-left */
djs_join2(djs, label, label - cols - 1);
break;
case 5: /* Left and up-left */
djs_join3(djs, label, label - 1, label - cols - 1);
break;
case 6: /* Up and up-left */
djs_join3(djs, label, label - cols, label - cols - 1);
break;
case 7: /* Left, up, and up-left */
djs_join4(djs, label, label - 1, label - cols, label - cols - 1);
break;
case 8: /* Up-right */
djs_join2(djs, label, label - cols + 1);
break;
case 9: /* Left and up-right */
djs_join3(djs, label, label - 1, label - cols + 1);
break;
case 10: /* Up and up-right */
djs_join3(djs, label, label - cols, label - cols + 1);
break;
case 11: /* Left, up, and up-right */
djs_join4(djs, label, label - 1, label - cols, label - cols + 1);
break;
case 12: /* Up-left and up-right */
djs_join3(djs, label, label - cols - 1, label - cols + 1);
break;
case 13: /* Left, up-left, and up-right */
djs_join4(djs, label, label - 1, label - cols - 1, label - cols + 1);
break;
case 14: /* Up, up-left, and up-right */
djs_join4(djs, label, label - cols, label - cols - 1, label - cols + 1);
break;
case 15: /* Left, up, up-left, and up-right */
djs_join5(djs, label, label - 1, label - cols, label - cols - 1, label - cols + 1);
break;
}
}
}
/* Count the occurrences of each disjoint-set root label. */
if (roots[0] && roots[1]) {
const size_t labels = rows * cols + 2;
/* Clear the counts. */
memset(roots[0], 0, labels * sizeof (cluster_label));
memset(roots[1], 0, labels * sizeof (cluster_label));
for (r = 0; r < rows; r++) {
const cluster_color *const curr_row = map + r * map_stride;
const cluster_label curr_i = r * cols;
for (c = 0; c < cols; c++)
roots[curr_row[c]][djs_flatten(djs, curr_i + c)]++;
}
} else {
size_t i = rows * cols;
while (i-->0)
djs_flatten(djs, i);
}
/* Collect the statistics. */
if (cl->white_histogram && roots[0]) {
const cluster_label *const root_count = roots[0];
cluster_count *const histogram = cl->white_histogram;
size_t i = rows * cols + 1;
while (i-->0)
histogram[root_count[i]]++;
}
if (cl->black_histogram && roots[1]) {
const cluster_label *const root_count = roots[1];
cluster_count *const histogram = cl->black_histogram;
size_t i = rows * cols + 1;
while (i-->0)
histogram[root_count[i]]++;
}
/* Note: index zero and (rows*cols+1) are zero in the histogram, for ease of scanning. */
if (cl->white_histogram || cl->black_histogram) {
const size_t n = rows * cols + 1;
if (cl->white_histogram) {
cl->white_histogram[0] = 0;
cl->white_histogram[n] = 0;
}
if (cl->black_histogram) {
cl->black_histogram[0] = 0;
cl->black_histogram[n] = 0;
}
}
/* One more iteration performed. */
cl->iterations++;
}
#endif /* CLUSTERS_H */
Вот пример программы, distribution.c , которая собирает статистику размера кластера за несколько итераций и выводит ее в формате, подходящем для построения, например, с помощью Gnuplot. Запустите его без аргументов, чтобы увидеть использование и помощь.
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include "clusters.h"
/* This file is in public domain. No guarantees, no warranties.
Written by Nominal Animal <question@nominal-animal.net>.
*/
#define DEFAULT_ROWS 100
#define DEFAULT_COLS 100
#define DEFAULT_P_BLACK 0.0
#define DEFAULT_D_WHITE 0.0
#define DEFAULT_D_BLACK 0.0
#define DEFAULT_ITERS 1
int usage(const char *argv0)
{
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
fprintf(stderr, " %s OPTIONS [ > output.txt ]\n", argv0);
fprintf(stderr, "\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " rows=SIZE Set number of rows. Default is %d.\n", DEFAULT_ROWS);
fprintf(stderr, " cols=SIZE Set number of columns. Default is %d.\n", DEFAULT_ROWS);
fprintf(stderr, " L=SIZE Set rows=SIZE and cols=SIZE.\n");
fprintf(stderr, " black=P Set the probability of a cell to be black. Default is %g.\n", DEFAULT_P_BLACK);
fprintf(stderr, " All non-black cells are white.\n");
fprintf(stderr, " dwhite=P Set the probability of white cells connecting diagonally.\n");
fprintf(stderr, " Default is %g.\n", DEFAULT_D_WHITE);
fprintf(stderr, " dblack=P Set the probability of black cells connecting diagonally.\n");
fprintf(stderr, " Default is %g.\n", DEFAULT_D_BLACK);
fprintf(stderr, " N=COUNT Number of iterations for gathering statistics. Default is %d.\n", DEFAULT_ITERS);
fprintf(stderr, " seed=U64 Set the Xorshift64* pseudorandom number generator seed; nonzero.\n");
fprintf(stderr, " Default is to pick one randomly (based on time).\n");
fprintf(stderr, "\n");
fprintf(stderr, "The output consists of comment lines and data lines.\n");
fprintf(stderr, "Comment lines begin with a #:\n");
fprintf(stderr, " # This is a comment line.\n");
fprintf(stderr, "Each data line contains a cluster size, the number of white clusters of that size\n");
fprintf(stderr, "observed during iterations, the number of black clusters of that size observed\n");
fprintf(stderr, "during iterations, and the number of any clusters of that size observed:\n");
fprintf(stderr, " SIZE WHITE_CLUSTERS BLACK_CLUSTERS TOTAL_CLUSTERS\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
int main(int argc, char *argv[])
{
int rows = DEFAULT_ROWS;
int cols = DEFAULT_COLS;
double p_black = DEFAULT_P_BLACK;
double d_white = DEFAULT_D_WHITE;
double d_black = DEFAULT_D_BLACK;
long iters = DEFAULT_ITERS;
uint64_t seed = 0;
cluster c = CLUSTER_INITIALIZER;
int arg, itemp;
uint64_t u64temp;
double dtemp;
long ltemp;
char dummy;
size_t n;
size_t i;
if (argc < 2)
return usage(argv[0]);
for (arg = 1; arg < argc; arg++)
if (!strcmp(argv[arg], "-h") || !strcmp(argv[arg], "/?") || !strcmp(argv[arg], "--help"))
return usage(argv[0]);
else
if (sscanf(argv[arg], "L=%d %c", &itemp, &dummy) == 1 ||
sscanf(argv[arg], "l=%d %c", &itemp, &dummy) == 1 ||
sscanf(argv[arg], "size=%d %c", &itemp, &dummy) == 1) {
rows = itemp;
cols = itemp;
} else
if (sscanf(argv[arg], "seed=%" SCNu64 " %c", &u64temp, &dummy) == 1 ||
sscanf(argv[arg], "seed=%" SCNx64 " %c", &u64temp, &dummy) == 1 ||
sscanf(argv[arg], "s=%" SCNu64 " %c", &u64temp, &dummy) == 1 ||
sscanf(argv[arg], "s=%" SCNx64 " %c", &u64temp, &dummy) == 1) {
seed = u64temp;
} else
if (sscanf(argv[arg], "N=%ld %c", <emp, &dummy) == 1 ||
sscanf(argv[arg], "n=%ld %c", <emp, &dummy) == 1 ||
sscanf(argv[arg], "count=%ld %c", <emp, &dummy) == 1) {
iters = ltemp;
} else
if (sscanf(argv[arg], "rows=%d %c", &itemp, &dummy) == 1 ||
sscanf(argv[arg], "r=%d %c", &itemp, &dummy) == 1 ||
sscanf(argv[arg], "height=%d %c", &itemp, &dummy) == 1 ||
sscanf(argv[arg], "h=%d %c", &itemp, &dummy) == 1) {
rows = itemp;
} else
if (sscanf(argv[arg], "columns=%d %c", &itemp, &dummy) == 1 ||
sscanf(argv[arg], "cols=%d %c", &itemp, &dummy) == 1 ||
sscanf(argv[arg], "c=%d %c", &itemp, &dummy) == 1 ||
sscanf(argv[arg], "width=%d %c", &itemp, &dummy) == 1 ||
sscanf(argv[arg], "w=%d %c", &itemp, &dummy) == 1) {
cols = itemp;
} else
if (sscanf(argv[arg], "black=%lf %c", &dtemp, &dummy) == 1 ||
sscanf(argv[arg], "p0=%lf %c", &dtemp, &dummy) == 1 ||
sscanf(argv[arg], "b=%lf %c", &dtemp, &dummy) == 1 ||
sscanf(argv[arg], "P=%lf %c", &dtemp, &dummy) == 1 ||
sscanf(argv[arg], "p0=%lf %c", &dtemp, &dummy) == 1 ||
sscanf(argv[arg], "p=%lf %c", &dtemp, &dummy) == 1) {
p_black = dtemp;
} else
if (sscanf(argv[arg], "white=%lf %c", &dtemp, &dummy) == 1 ||
sscanf(argv[arg], "p1=%lf %c", &dtemp, &dummy) == 1) {
p_black = 1.0 - dtemp;
} else
if (sscanf(argv[arg], "dwhite=%lf %c", &dtemp, &dummy) == 1 ||
sscanf(argv[arg], "dw=%lf %c", &dtemp, &dummy) == 1 ||
sscanf(argv[arg], "d0=%lf %c", &dtemp, &dummy) == 1) {
d_white = dtemp;
} else
if (sscanf(argv[arg], "dblack=%lf %c", &dtemp, &dummy) == 1 ||
sscanf(argv[arg], "db=%lf %c", &dtemp, &dummy) == 1 ||
sscanf(argv[arg], "d1=%lf %c", &dtemp, &dummy) == 1) {
d_black = dtemp;
} else {
fprintf(stderr, "%s: Unknown option.\n", argv[arg]);
return EXIT_FAILURE;
}
switch (init_cluster(&c, rows, cols, p_black, d_white, d_black)) {
case 0: break; /* OK */
case ERR_INVALID:
fprintf(stderr, "Invalid size.\n");
return EXIT_FAILURE;
case ERR_TOOLARGE:
fprintf(stderr, "Size is too large.\n");
return EXIT_FAILURE;
case ERR_NOMEM:
fprintf(stderr, "Not enough memory.\n");
return EXIT_FAILURE;
}
if (!seed)
seed = randomize(NULL);
c.rng.state = seed;
/* The largest possible cluster has n cells. */
n = (size_t)rows * (size_t)cols;
/* Print the comments describing the initial parameters. */
printf("# seed: %" PRIu64 " (Xorshift 64*)\n", seed);
printf("# size: %d rows, %d columns\n", rows, cols);
printf("# P(black): %.6f (%" PRIu64 "/18446744073709551615)\n", p_black, c.p_black);
printf("# P(black connected diagonally): %.6f (%" PRIu64 "/18446744073709551615)\n", d_black, c.d_black);
printf("# P(white connected diagonally): %.6f (%" PRIu64 "/18446744073709551615)\n", d_white, c.d_white);
fflush(stdout);
while (iters-->0)
iterate(&c);
printf("# Iterations: %" PRIu64 "\n", c.iterations);
printf("#\n");
printf("# size white_clusters(size) black_clusters(size) clusters(size)\n");
/* Note: c._histogram[0] == c._histogram[n] == 0, for ease of scanning. */
for (i = 1; i <= n; i++)
if (c.white_histogram[i-1] || c.white_histogram[i] || c.white_histogram[i+1] ||
c.black_histogram[i-1] || c.black_histogram[i] || c.black_histogram[i+1])
printf("%lu %" FMT_COUNT " %" FMT_COUNT " %" FMT_COUNT "\n",
(unsigned long)i,
c.white_histogram[i],
c.black_histogram[i],
c.white_histogram[i]+c.black_histogram[i]);
/* Since we are exiting anyway, this is not really necessary. */
free_cluster(&c);
/* All done. */
return EXIT_SUCCESS;
}
Вот график, сгенерированный запуском distribution.c с L=100 black=0.5 dwhite=0 dblack=0.25 seed=20120622 N=100000
(распределения размера кластера, собранного из сотен тысяч матриц 100x100, где вероятность ненулевой ячейки составляет 50%, а вероятность подключения ненулевых ячеек на 25% по диагонали, нулевые ячейки никогда не соединяются по диагонали, только по горизонтали и вертикали):
Как видите, поскольку ненулевые кластеры могут иногда соединяться и по диагонали, ненулевых кластеров больше, чем нулевых. Вычисление заняло около 37 секунд на ноутбуке i5-7200U. Поскольку начальное значение Xorshift64 * задано явно, это повторяемый тест.