w-cluster 1.0.20 → 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/.github/workflows/ci-test.yml +3 -3
  2. package/README.md +4 -6
  3. package/babel.config.js +4 -3
  4. package/dist/w-cluster.umd.js +2 -2
  5. package/dist/w-cluster.umd.js.map +1 -1
  6. package/dist/w-cluster.wk.umd.js +1 -1
  7. package/docs/DenseMatrix.html +202 -0
  8. package/docs/DistancePair.html +202 -0
  9. package/docs/LowerTriangle.html +202 -0
  10. package/docs/Rec.html +202 -0
  11. package/docs/Reco.html +202 -0
  12. package/docs/WCluster.mjs.html +8 -4
  13. package/docs/examples/ex-PCA.html +1 -1
  14. package/docs/examples/ex-cluster-webworker.html +1 -1
  15. package/docs/examples/ex-cluster.html +1 -1
  16. package/docs/global.html +6799 -164
  17. package/docs/index.html +2 -2
  18. package/docs/k-medoids_alternating.mjs.html +144 -0
  19. package/docs/k-medoids_arrayadapter.mjs.html +107 -0
  20. package/docs/k-medoids_dynmsc.mjs.html +255 -0
  21. package/docs/k-medoids_fastermsc.mjs.html +411 -0
  22. package/docs/k-medoids_fasterpam.mjs.html +305 -0
  23. package/docs/k-medoids_fastmsc.mjs.html +166 -0
  24. package/docs/k-medoids_fastpam1.mjs.html +132 -0
  25. package/docs/k-medoids_initialization.mjs.html +107 -0
  26. package/docs/k-medoids_pam.mjs.html +275 -0
  27. package/docs/k-medoids_pammedsil.mjs.html +307 -0
  28. package/docs/k-medoids_pamsil.mjs.html +225 -0
  29. package/docs/k-medoids_par_fasterpam.mjs.html +134 -0
  30. package/docs/k-medoids_par_silhouette.mjs.html +126 -0
  31. package/docs/k-medoids_silhouette.mjs.html +193 -0
  32. package/docs/k-medoids_util.mjs.html +135 -0
  33. package/g-PCA-nodeworker.mjs +1 -1
  34. package/g-PCA.mjs +1 -1
  35. package/g-cluster-kMeans-large.mjs +83 -0
  36. package/g-cluster-kMeans-nodeworker.mjs +181 -0
  37. package/g-cluster-kMeans.mjs +178 -0
  38. package/g-cluster-kMedoids-large-suggest.mjs +95 -0
  39. package/g-cluster-kMedoids-large.mjs +104 -0
  40. package/{g-cluster-nodeworker.mjs → g-cluster-kMedoids-nodeworker.mjs} +1 -2
  41. package/{g-cluster.mjs → g-cluster-kMedoids-simple.mjs} +1 -1
  42. package/package.json +7 -7
  43. package/script.txt +5 -7
  44. package/src/WCluster.mjs +6 -2
  45. package/src/WClusterCore.mjs +30 -21
  46. package/src/WClusterMat.mjs +98 -24
  47. package/src/jaccardBitset.mjs +63 -0
  48. package/src/k-medoids/README.md +124 -0
  49. package/src/k-medoids/alternating.mjs +72 -0
  50. package/src/k-medoids/arrayadapter.mjs +35 -0
  51. package/src/k-medoids/dynmsc.mjs +183 -0
  52. package/src/k-medoids/fastermsc.mjs +339 -0
  53. package/src/k-medoids/fasterpam.mjs +233 -0
  54. package/src/k-medoids/fastmsc.mjs +94 -0
  55. package/src/k-medoids/fastpam1.mjs +60 -0
  56. package/src/k-medoids/index.mjs +37 -0
  57. package/src/k-medoids/initialization.mjs +35 -0
  58. package/src/k-medoids/package.json +8 -0
  59. package/src/k-medoids/pam.mjs +203 -0
  60. package/src/k-medoids/pammedsil.mjs +235 -0
  61. package/src/k-medoids/pamsil.mjs +153 -0
  62. package/src/k-medoids/par_fasterpam.mjs +62 -0
  63. package/src/k-medoids/par_silhouette.mjs +54 -0
  64. package/src/k-medoids/silhouette.mjs +121 -0
  65. package/src/k-medoids/test.mjs +357 -0
  66. package/src/k-medoids/util.mjs +63 -0
  67. package/test/jaccardBitset.test.mjs +141 -0
  68. package/test/kMeans.test.mjs +278 -0
  69. package/test/kMedoids-large-suggest.test.mjs +140 -0
  70. package/test/kMedoids-large.test.mjs +142 -0
  71. package/test/{cluster.test.mjs → kMedoids-simple.test.mjs} +5 -5
  72. package/toolg/gDocsExams.mjs +1 -1
@@ -0,0 +1,107 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>k-medoids/initialization.mjs - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ </head>
20
+ <body>
21
+
22
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
23
+ <label for="nav-trigger" class="navicon-button x">
24
+ <div class="navicon"></div>
25
+ </label>
26
+
27
+ <label for="nav-trigger" class="overlay"></label>
28
+
29
+ <nav >
30
+
31
+
32
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="DenseMatrix.html">DenseMatrix</a></li><li><a href="DistancePair.html">DistancePair</a></li><li><a href="LowerTriangle.html">LowerTriangle</a></li><li><a href="Rec.html">Rec</a></li><li><a href="Reco.html">Reco</a></li></ul><h3>Global</h3><ul><li><a href="global.html#PCA">PCA</a></li><li><a href="global.html#WCluster">WCluster</a></li><li><a href="global.html#alternating">alternating</a></li><li><a href="global.html#arrayAdapter">arrayAdapter</a></li><li><a href="global.html#assign_nearest">assign_nearest</a></li><li><a href="global.html#choose_medoid_within_partition">choose_medoid_within_partition</a></li><li><a href="global.html#cluster">cluster</a></li><li><a href="global.html#do_swap">do_swap</a></li><li><a href="global.html#do_swap_k2">do_swap_k2</a></li><li><a href="global.html#dynmsc">dynmsc</a></li><li><a href="global.html#fastermsc">fastermsc</a></li><li><a href="global.html#fastermsc_k2">fastermsc_k2</a></li><li><a href="global.html#fasterpam">fasterpam</a></li><li><a href="global.html#fastmsc">fastmsc</a></li><li><a href="global.html#fastmsc_k2">fastmsc_k2</a></li><li><a href="global.html#fastpam1">fastpam1</a></li><li><a href="global.html#find_best_swap">find_best_swap</a></li><li><a href="global.html#find_best_swap_k2">find_best_swap_k2</a></li><li><a href="global.html#find_best_swap_pam">find_best_swap_pam</a></li><li><a href="global.html#find_best_swap_pammedsil">find_best_swap_pammedsil</a></li><li><a href="global.html#find_best_swap_pammedsil_k2">find_best_swap_pammedsil_k2</a></li><li><a href="global.html#find_max">find_max</a></li><li><a href="global.html#find_min">find_min</a></li><li><a href="global.html#first_k">first_k</a></li><li><a href="global.html#initial_assignment">initial_assignment</a></li><li><a href="global.html#initial_assignment_k2">initial_assignment_k2</a></li><li><a href="global.html#medoid_silhouette">medoid_silhouette</a></li><li><a href="global.html#pam">pam</a></li><li><a href="global.html#pam_build">pam_build</a></li><li><a href="global.html#pam_build_initialize">pam_build_initialize</a></li><li><a href="global.html#pam_optimize">pam_optimize</a></li><li><a href="global.html#pam_swap">pam_swap</a></li><li><a href="global.html#pammedsil">pammedsil</a></li><li><a href="global.html#pammedsil_build_initialize">pammedsil_build_initialize</a></li><li><a href="global.html#pammedsil_optimize">pammedsil_optimize</a></li><li><a href="global.html#pammedsil_swap">pammedsil_swap</a></li><li><a href="global.html#pamsil">pamsil</a></li><li><a href="global.html#pamsil_build_initialize">pamsil_build_initialize</a></li><li><a href="global.html#pamsil_optimize">pamsil_optimize</a></li><li><a href="global.html#pamsil_swap">pamsil_swap</a></li><li><a href="global.html#par_fasterpam">par_fasterpam</a></li><li><a href="global.html#par_silhouette">par_silhouette</a></li><li><a href="global.html#rand_fasterpam">rand_fasterpam</a></li><li><a href="global.html#random_initialization">random_initialization</a></li><li><a href="global.html#remove_med">remove_med</a></li><li><a href="global.html#sample">sample</a></li><li><a href="global.html#shuffle">shuffle</a></li><li><a href="global.html#silhouette">silhouette</a></li><li><a href="global.html#update_removal_loss">update_removal_loss</a></li><li><a href="global.html#update_second_nearest">update_second_nearest</a></li><li><a href="global.html#update_third_nearest">update_third_nearest</a></li><li><a href="global.html#update_third_nearest_without_new">update_third_nearest_without_new</a></li></ul>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">k-medoids/initialization.mjs</h1>
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ <section>
47
+ <article>
48
+ <pre class="prettyprint source linenums"><code>// Initialization helpers, ported from src/initialization.rs (RNG approximated for JS)
49
+
50
+ /** Use the first k objects (0..k-1) as initial medoids. */
51
+ export function first_k(k) {
52
+ return Array.from({ length: k }, (_, i) => i);
53
+ }
54
+
55
+ /** Sample k DISTINCT indices from [0, n) using Floyd's algorithm. rng: () => float in [0,1). */
56
+ export function sample(rng, n, k) {
57
+ if (k > n) throw new Error('`amount` of samples must be less than or equal to `length`');
58
+ const chosen = new Set();
59
+ const res = [];
60
+ for (let j = n - k; j &lt; n; j++) {
61
+ const t = Math.floor(rng() * (j + 1)); // 0..j inclusive
62
+ const v = chosen.has(t) ? j : t;
63
+ chosen.add(v);
64
+ res.push(v);
65
+ }
66
+ return res;
67
+ }
68
+
69
+ /** Random initialization: k distinct medoid indices in [0, n). */
70
+ export function random_initialization(n, k, rng = Math.random) {
71
+ return sample(rng, n, k);
72
+ }
73
+
74
+ /** Fisher-Yates shuffle of [0, n). Used by rand_fasterpam / par_fasterpam. */
75
+ export function shuffle(rng, n) {
76
+ const a = Array.from({ length: n }, (_, i) => i);
77
+ for (let i = n - 1; i > 0; i--) {
78
+ const j = Math.floor(rng() * (i + 1));
79
+ const tmp = a[i]; a[i] = a[j]; a[j] = tmp;
80
+ }
81
+ return a;
82
+ }
83
+ </code></pre>
84
+ </article>
85
+ </section>
86
+
87
+
88
+
89
+
90
+
91
+
92
+ </div>
93
+
94
+ <br class="clear">
95
+
96
+ <footer>
97
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Jun 14 2026 10:59:21 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
98
+ </footer>
99
+
100
+ <script>prettyPrint();</script>
101
+ <script src="scripts/polyfill.js"></script>
102
+ <script src="scripts/linenumber.js"></script>
103
+
104
+
105
+
106
+ </body>
107
+ </html>
@@ -0,0 +1,275 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>k-medoids/pam.mjs - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ </head>
20
+ <body>
21
+
22
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
23
+ <label for="nav-trigger" class="navicon-button x">
24
+ <div class="navicon"></div>
25
+ </label>
26
+
27
+ <label for="nav-trigger" class="overlay"></label>
28
+
29
+ <nav >
30
+
31
+
32
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="DenseMatrix.html">DenseMatrix</a></li><li><a href="DistancePair.html">DistancePair</a></li><li><a href="LowerTriangle.html">LowerTriangle</a></li><li><a href="Rec.html">Rec</a></li><li><a href="Reco.html">Reco</a></li></ul><h3>Global</h3><ul><li><a href="global.html#PCA">PCA</a></li><li><a href="global.html#WCluster">WCluster</a></li><li><a href="global.html#alternating">alternating</a></li><li><a href="global.html#arrayAdapter">arrayAdapter</a></li><li><a href="global.html#assign_nearest">assign_nearest</a></li><li><a href="global.html#choose_medoid_within_partition">choose_medoid_within_partition</a></li><li><a href="global.html#cluster">cluster</a></li><li><a href="global.html#do_swap">do_swap</a></li><li><a href="global.html#do_swap_k2">do_swap_k2</a></li><li><a href="global.html#dynmsc">dynmsc</a></li><li><a href="global.html#fastermsc">fastermsc</a></li><li><a href="global.html#fastermsc_k2">fastermsc_k2</a></li><li><a href="global.html#fasterpam">fasterpam</a></li><li><a href="global.html#fastmsc">fastmsc</a></li><li><a href="global.html#fastmsc_k2">fastmsc_k2</a></li><li><a href="global.html#fastpam1">fastpam1</a></li><li><a href="global.html#find_best_swap">find_best_swap</a></li><li><a href="global.html#find_best_swap_k2">find_best_swap_k2</a></li><li><a href="global.html#find_best_swap_pam">find_best_swap_pam</a></li><li><a href="global.html#find_best_swap_pammedsil">find_best_swap_pammedsil</a></li><li><a href="global.html#find_best_swap_pammedsil_k2">find_best_swap_pammedsil_k2</a></li><li><a href="global.html#find_max">find_max</a></li><li><a href="global.html#find_min">find_min</a></li><li><a href="global.html#first_k">first_k</a></li><li><a href="global.html#initial_assignment">initial_assignment</a></li><li><a href="global.html#initial_assignment_k2">initial_assignment_k2</a></li><li><a href="global.html#medoid_silhouette">medoid_silhouette</a></li><li><a href="global.html#pam">pam</a></li><li><a href="global.html#pam_build">pam_build</a></li><li><a href="global.html#pam_build_initialize">pam_build_initialize</a></li><li><a href="global.html#pam_optimize">pam_optimize</a></li><li><a href="global.html#pam_swap">pam_swap</a></li><li><a href="global.html#pammedsil">pammedsil</a></li><li><a href="global.html#pammedsil_build_initialize">pammedsil_build_initialize</a></li><li><a href="global.html#pammedsil_optimize">pammedsil_optimize</a></li><li><a href="global.html#pammedsil_swap">pammedsil_swap</a></li><li><a href="global.html#pamsil">pamsil</a></li><li><a href="global.html#pamsil_build_initialize">pamsil_build_initialize</a></li><li><a href="global.html#pamsil_optimize">pamsil_optimize</a></li><li><a href="global.html#pamsil_swap">pamsil_swap</a></li><li><a href="global.html#par_fasterpam">par_fasterpam</a></li><li><a href="global.html#par_silhouette">par_silhouette</a></li><li><a href="global.html#rand_fasterpam">rand_fasterpam</a></li><li><a href="global.html#random_initialization">random_initialization</a></li><li><a href="global.html#remove_med">remove_med</a></li><li><a href="global.html#sample">sample</a></li><li><a href="global.html#shuffle">shuffle</a></li><li><a href="global.html#silhouette">silhouette</a></li><li><a href="global.html#update_removal_loss">update_removal_loss</a></li><li><a href="global.html#update_second_nearest">update_second_nearest</a></li><li><a href="global.html#update_third_nearest">update_third_nearest</a></li><li><a href="global.html#update_third_nearest_without_new">update_third_nearest_without_new</a></li></ul>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">k-medoids/pam.mjs</h1>
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ <section>
47
+ <article>
48
+ <pre class="prettyprint source linenums"><code>// PAM (BUILD and SWAP) algorithm, ported 1:1 from src/pam.rs
49
+ import { arrayAdapter } from './arrayadapter.mjs';
50
+ import { Rec, DistancePair, U32_MAX, USIZE_MAX, choose_medoid_within_partition } from './util.mjs';
51
+ import { initial_assignment, do_swap } from './fasterpam.mjs';
52
+
53
+ /**
54
+ * Run the original PAM SWAP algorithm (no BUILD, but given initial medoids).
55
+ *
56
+ * Provided for academic reasons to see the performance difference.
57
+ *
58
+ * @param mat - a pairwise distance matrix
59
+ * @param med - the list of medoids (mutated in place)
60
+ * @param maxiter - the maximum number of iterations allowed
61
+ * @returns { loss, assi, nIter, nSwaps }
62
+ */
63
+ export function pam_swap(mat, med, maxiter) {
64
+ mat = arrayAdapter(mat);
65
+ const [loss, data] = initial_assignment(mat, med);
66
+ const [nloss, assi, nIter, nSwaps] = pam_optimize(mat, med, data, maxiter, loss);
67
+ return { loss: nloss, assi, nIter, nSwaps };
68
+ }
69
+
70
+ /**
71
+ * Run the original PAM BUILD algorithm.
72
+ *
73
+ * Provided for academic reasons to see the performance difference.
74
+ *
75
+ * @param mat - a pairwise distance matrix
76
+ * @param k - the number of medoids to pick
77
+ * @returns { loss, assi, meds }
78
+ */
79
+ export function pam_build(mat, k) {
80
+ mat = arrayAdapter(mat);
81
+ const n = mat.len();
82
+ if (!mat.isSquare()) throw new Error('Dissimilarity matrix is not square');
83
+ if (!(n &lt;= U32_MAX)) throw new Error('N is too large');
84
+ if (!(k > 0 &amp;&amp; k &lt; U32_MAX)) throw new Error('invalid N');
85
+ if (!(k &lt;= n)) throw new Error('k must be at most N');
86
+ const meds = [];
87
+ const data = [];
88
+ const loss = pam_build_initialize(mat, meds, data, k);
89
+ const assi = data.map((x) => x.near.i);
90
+ return { loss, assi, meds };
91
+ }
92
+
93
+ /**
94
+ * Run the original PAM algorithm (BUILD and SWAP).
95
+ *
96
+ * Provided for academic reasons to see the performance difference.
97
+ *
98
+ * @param mat - a pairwise distance matrix
99
+ * @param k - the number of medoids to pick
100
+ * @param maxiter - the maximum number of iterations allowed
101
+ * @returns { loss, assi, meds, nIter, nSwaps }
102
+ */
103
+ export function pam(mat, k, maxiter) {
104
+ mat = arrayAdapter(mat);
105
+ const n = mat.len();
106
+ if (!mat.isSquare()) throw new Error('Dissimilarity matrix is not square');
107
+ if (!(n &lt;= U32_MAX)) throw new Error('N is too large');
108
+ if (!(k > 0 &amp;&amp; k &lt; U32_MAX)) throw new Error('invalid N');
109
+ if (!(k &lt;= n)) throw new Error('k must be at most N');
110
+ const meds = [];
111
+ const data = [];
112
+ const loss = pam_build_initialize(mat, meds, data, k);
113
+ const [nloss, assi, nIter, nSwaps] = pam_optimize(mat, meds, data, maxiter, loss);
114
+ return { loss: nloss, assi, meds, nIter, nSwaps }; // also return medoids
115
+ }
116
+
117
+ /** Main optimization function of PAM, not exposed (use pam_swap or pam). Returns [loss, assi, nIter, nSwaps]. */
118
+ function pam_optimize(mat, med, data, maxiter, loss) {
119
+ const n = mat.len(), k = med.length;
120
+ if (k === 1) {
121
+ const assi = new Array(n).fill(0);
122
+ const [swapped, sloss] = choose_medoid_within_partition(mat, assi, med, 0);
123
+ return [sloss, assi, 1, swapped ? 1 : 0];
124
+ }
125
+ let n_swaps = 0, iter = 0;
126
+ while (iter &lt; maxiter) {
127
+ iter += 1;
128
+ let best = [0, k, USIZE_MAX];
129
+ for (let j = 0; j &lt; n; j++) {
130
+ if (j === med[data[j].near.i]) {
131
+ continue; // This already is a medoid
132
+ }
133
+ const [change, b] = find_best_swap_pam(mat, med, data, j);
134
+ if (change >= best[0]) {
135
+ continue; // No improvement
136
+ }
137
+ best = [change, b, j];
138
+ }
139
+ if (best[0] &lt; 0) {
140
+ n_swaps += 1;
141
+ // perform the swap
142
+ const newloss = do_swap(mat, med, data, best[1], best[2]);
143
+ if (newloss >= loss) {
144
+ break; // Probably numerically unstable now.
145
+ }
146
+ loss = newloss;
147
+ } else {
148
+ break; // No improvement, or NaN.
149
+ }
150
+ }
151
+ const assi = data.map((x) => x.near.i);
152
+ return [loss, assi, iter, n_swaps];
153
+ }
154
+
155
+ /** Find the best swap for object j - slower PAM version. Returns [acc_best, m_best]. */
156
+ function find_best_swap_pam(mat, med, data, j) {
157
+ const recj = data[j];
158
+ let best = [0, USIZE_MAX];
159
+ for (let m = 0; m &lt; med.length; m++) {
160
+ let acc = -recj.near.d; // j becomes medoid
161
+ for (let o = 0; o &lt; data.length; o++) {
162
+ const reco = data[o];
163
+ if (o === j) {
164
+ continue;
165
+ }
166
+ const doj = mat.get(o, j);
167
+ // Current medoid is being replaced:
168
+ if (reco.near.i === m) {
169
+ if (doj &lt; reco.seco.d) {
170
+ // Assign to new medoid:
171
+ acc += doj - reco.near.d;
172
+ } else {
173
+ // Assign to second nearest instead:
174
+ acc += reco.seco.d - reco.near.d;
175
+ }
176
+ } else if (doj &lt; reco.near.d) {
177
+ // new mediod is closer:
178
+ acc += doj - reco.near.d;
179
+ } // else no change
180
+ }
181
+ if (acc &lt; best[0]) {
182
+ best = [acc, m];
183
+ }
184
+ }
185
+ return best;
186
+ }
187
+
188
+ /** Not exposed. Use pam_build or pam. Pushes into medsArr (numbers) and dataArr (Rec). Returns loss. */
189
+ export function pam_build_initialize(mat, meds, data, k) {
190
+ const n = mat.len();
191
+ if (!mat.isSquare()) throw new Error('Dissimilarity matrix is not square');
192
+ // choose first medoid
193
+ let best = [0, k];
194
+ for (let i = 0; i &lt; n; i++) {
195
+ let sum = 0;
196
+ for (let j = 0; j &lt; n; j++) {
197
+ if (j !== i) {
198
+ sum += mat.get(j, i);
199
+ }
200
+ }
201
+ if (i === 0 || sum &lt; best[0]) {
202
+ best = [sum, i];
203
+ }
204
+ }
205
+ let loss = best[0];
206
+ meds.push(best[1]);
207
+ for (let j = 0; j &lt; n; j++) {
208
+ data.push(new Rec(0, mat.get(j, best[1]), U32_MAX, 0));
209
+ }
210
+ // choose remaining medoids
211
+ for (let l = 1; l &lt; k; l++) {
212
+ best = [0, k];
213
+ for (let i = 0; i &lt; data.length; i++) {
214
+ let sum = -data[i].near.d;
215
+ for (let j = 0; j &lt; data.length; j++) {
216
+ const dj = data[j];
217
+ if (j !== i) {
218
+ const d = mat.get(j, i);
219
+ if (d &lt; dj.near.d) {
220
+ sum += d - dj.near.d;
221
+ }
222
+ }
223
+ }
224
+ if (i === 0 || sum &lt; best[0]) {
225
+ best = [sum, i];
226
+ }
227
+ }
228
+ if (best[0] >= 0) { break; } // No further improvements - duplicates etc.
229
+ // Update assignments:
230
+ loss = 0;
231
+ for (let j = 0; j &lt; data.length; j++) {
232
+ const recj = data[j];
233
+ if (j === best[1]) {
234
+ recj.seco = recj.near.clone();
235
+ recj.near = new DistancePair(l, 0);
236
+ continue;
237
+ }
238
+ const dj = mat.get(j, best[1]);
239
+ if (dj &lt; recj.near.d) {
240
+ recj.seco = recj.near.clone();
241
+ recj.near = new DistancePair(l, dj);
242
+ } else if (recj.seco.i === U32_MAX || dj &lt; recj.seco.d) {
243
+ recj.seco = new DistancePair(l, dj);
244
+ }
245
+ loss += recj.near.d;
246
+ }
247
+ meds.push(best[1]);
248
+ }
249
+ return loss;
250
+ }
251
+ </code></pre>
252
+ </article>
253
+ </section>
254
+
255
+
256
+
257
+
258
+
259
+
260
+ </div>
261
+
262
+ <br class="clear">
263
+
264
+ <footer>
265
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Jun 14 2026 10:59:21 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
266
+ </footer>
267
+
268
+ <script>prettyPrint();</script>
269
+ <script src="scripts/polyfill.js"></script>
270
+ <script src="scripts/linenumber.js"></script>
271
+
272
+
273
+
274
+ </body>
275
+ </html>