KursblockungMatrix.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.cast_de_nrw_schule_svws_core_kursblockung_KursblockungMatrix = exports.KursblockungMatrix = void 0;
  4. const JavaObject_1 = require("../../java/lang/JavaObject");
  5. const Random_1 = require("../../java/util/Random");
  6. const StringBuilder_1 = require("../../java/lang/StringBuilder");
  7. const Arrays_1 = require("../../java/util/Arrays");
  8. const System_1 = require("../../java/lang/System");
  9. class KursblockungMatrix extends JavaObject_1.JavaObject {
  10. RND = new Random_1.Random();
  11. matrix;
  12. rows;
  13. cols;
  14. permR;
  15. permC;
  16. r2c;
  17. c2r;
  18. besuchtR;
  19. abgearbeitetC;
  20. vorgaengerCzuR;
  21. queueR;
  22. potentialR;
  23. potentialC;
  24. distanzC;
  25. /**
  26. * Erzeugt eine neue Matrix mit {@code rows} Zeilen und {@code cols} Spalten.
  27. *
  28. * @param rows Die Anzahl der Zeilen der Matrix.
  29. * @param cols Die Anzahl der Spalten der Matrix.
  30. */
  31. constructor(rows, cols) {
  32. super();
  33. this.rows = rows;
  34. this.cols = cols;
  35. this.matrix = [...Array(rows)].map(e => Array(cols).fill(0));
  36. this.permR = Array(rows).fill(0);
  37. this.permC = Array(cols).fill(0);
  38. this.r2c = Array(rows).fill(0);
  39. this.c2r = Array(cols).fill(0);
  40. this.besuchtR = Array(rows).fill(false);
  41. this.abgearbeitetC = Array(cols).fill(false);
  42. this.vorgaengerCzuR = Array(cols).fill(0);
  43. this.queueR = Array(rows).fill(0);
  44. this.potentialR = Array(rows).fill(0);
  45. this.potentialC = Array(cols).fill(0);
  46. this.distanzC = Array(cols).fill(0);
  47. KursblockungMatrix.initialisiere(this.permR);
  48. KursblockungMatrix.initialisiere(this.permC);
  49. }
  50. /**
  51. * Berechnet zur aktuellen Matrix ein maximales bipartites Matching. Die Methode
  52. * geht davon aus, dass in der Matrix ausschließlich die Werte 0 und 1
  53. * vorkommen. Werte ungleich 0 werden andernfalls als 1 (eine Kante im Graphen)
  54. * interpretiert. Nichtquadratische Matrizen sind erlaubt. Das Ergebnis der
  55. * Methode ist eine größtmögliche Zeilen- zu Spaltenzuordnung. Der Algorithmus
  56. * hat eine Laufzeit von O(n³).
  57. *
  58. * @param nichtdeterministisch definiert, ob das Ergebnis zufällig sein soll,
  59. * falls es mehrere optimale Lösungen gibt.
  60. *
  61. * @return die Zeilen- zu Spaltenzuordnung, negative Werte entsprechen einer
  62. * Nichtzuordnung.
  63. */
  64. gibMaximalesBipartitesMatching(nichtdeterministisch) {
  65. Arrays_1.Arrays.fill(this.r2c, -1);
  66. Arrays_1.Arrays.fill(this.c2r, -1);
  67. this.initialisierPermRundPermC(nichtdeterministisch);
  68. for (let pseudoR = 0; pseudoR < this.rows; pseudoR++) {
  69. let r = this.permR[pseudoR];
  70. Arrays_1.Arrays.fill(this.besuchtR, false);
  71. Arrays_1.Arrays.fill(this.vorgaengerCzuR, -1);
  72. let queue_first = 0;
  73. let queue_last = 0;
  74. this.queueR[queue_last] = r;
  75. queue_last++;
  76. this.besuchtR[r] = true;
  77. while (queue_first < queue_last) {
  78. let vonR = this.queueR[queue_first];
  79. queue_first++;
  80. for (let pseudoC = 0; pseudoC < this.cols; pseudoC++) {
  81. let ueberC = this.permC[pseudoC];
  82. if ((this.matrix[vonR][ueberC] !== 0) && (this.r2c[vonR] !== ueberC)) {
  83. let zuR = this.c2r[ueberC];
  84. if (zuR === -1) {
  85. this.vorgaengerCzuR[ueberC] = vonR;
  86. for (let c2 = ueberC; c2 >= 0;) {
  87. let r2 = this.vorgaengerCzuR[c2];
  88. let saveC = this.r2c[r2];
  89. this.c2r[c2] = r2;
  90. this.r2c[r2] = c2;
  91. c2 = saveC;
  92. }
  93. queue_last = queue_first;
  94. break;
  95. }
  96. if (this.besuchtR[zuR] === false) {
  97. this.besuchtR[zuR] = true;
  98. this.queueR[queue_last] = zuR;
  99. queue_last++;
  100. this.vorgaengerCzuR[ueberC] = vonR;
  101. }
  102. }
  103. }
  104. }
  105. }
  106. return this.r2c;
  107. }
  108. /**
  109. * Berechnet zur aktuellen Matrix ein minimales gewichtetes Matching. Die
  110. * Methode geht davon aus, dass in der Matrix ganzzahlige Werte vorkommen, d.h.
  111. * es existiert eine Kante von jedem linken Knoten zu jedem rechten Knoten.
  112. * Negative Werte und nichtquadratische Matrizen sind erlaubt. Zur Berechnung
  113. * eines maximalen Matching kann man vorher alle Zellenwerte negieren. Das
  114. * Ergebnis der Methode ist eine Zeilen- zu Spaltenzuordnung, deren Summe
  115. * minimal ist. Der Algorithmus verwendet mehrere Runden eines SSSP-Algorithmus
  116. * (Dijkstra). Damit dies bei negativen Werten funktioniert, werden die Kanten
  117. * mit Hilfe von Knoten-Potentialen umgewichtet. Der Algorithmus hat eine
  118. * Laufzeit von O(n³).
  119. *
  120. * @see <a href= "https://en.wikipedia.org/wiki/Shortest_path_problem">Wikipedia
  121. * - Shortest_path_problem</a>
  122. *
  123. * @see <a href= "https://en.wikipedia.org/wiki/Johnson%27s_algorithm">Wikipedia
  124. * - Johnsons Algorithm</a>
  125. *
  126. * @param nichtdeterministisch definiert, ob das Ergebnis zufällig sein soll,
  127. * falls es mehrere optimale Lösungen gibt.
  128. *
  129. * @return die Zeilen- zu Spaltenzuordnung, negative Werte entsprechen einer
  130. * Nichtzuordnung.
  131. */
  132. gibMinimalesBipartitesMatchingGewichtet(nichtdeterministisch) {
  133. Arrays_1.Arrays.fill(this.r2c, -1);
  134. Arrays_1.Arrays.fill(this.c2r, -1);
  135. Arrays_1.Arrays.fill(this.potentialR, 0);
  136. Arrays_1.Arrays.fill(this.potentialC, 0);
  137. this.initialisierPermRundPermC(nichtdeterministisch);
  138. if (this.rows <= this.cols) {
  139. for (let r = 0; r < this.rows; r++) {
  140. let min = this.matrix[r][0] + this.potentialR[r] - this.potentialC[0];
  141. for (let c = 0; c < this.cols; c++) {
  142. let kante = this.matrix[r][c] + this.potentialR[r] - this.potentialC[c];
  143. min = Math.min(min, kante);
  144. }
  145. this.potentialR[r] -= min;
  146. }
  147. }
  148. if (this.cols <= this.rows) {
  149. for (let c = 0; c < this.cols; c++) {
  150. let min = this.matrix[0][c] + this.potentialR[0] - this.potentialC[c];
  151. for (let r = 0; r < this.rows; r++) {
  152. let kante = this.matrix[r][c] + this.potentialR[r] - this.potentialC[c];
  153. min = Math.min(min, kante);
  154. }
  155. this.potentialC[c] += min;
  156. }
  157. }
  158. let dijkstraRunden = Math.min(this.rows, this.cols);
  159. Arrays_1.Arrays.fill(this.abgearbeitetC, false);
  160. for (let ir = 0; ir < this.rows; ir++) {
  161. let r = this.permR[ir];
  162. for (let ic = 0; ic < this.cols; ic++) {
  163. let c = this.permC[ic];
  164. if (!this.abgearbeitetC[c]) {
  165. let kante = this.matrix[r][c] + this.potentialR[r] - this.potentialC[c];
  166. if (kante === 0) {
  167. this.r2c[r] = c;
  168. this.c2r[c] = r;
  169. this.abgearbeitetC[c] = true;
  170. dijkstraRunden--;
  171. break;
  172. }
  173. }
  174. }
  175. }
  176. for (let dijkstraRunde = 0; dijkstraRunde < dijkstraRunden; dijkstraRunde++) {
  177. for (let ic = 0; ic < this.cols; ic++) {
  178. let c = this.permC[ic];
  179. this.vorgaengerCzuR[c] = -1;
  180. this.abgearbeitetC[c] = false;
  181. this.distanzC[c] = Number.MAX_VALUE;
  182. for (let ir = 0; ir < this.rows; ir++) {
  183. let r = this.permR[ir];
  184. if (this.r2c[r] < 0) {
  185. let kante = this.matrix[r][c] + this.potentialR[r] - this.potentialC[c];
  186. if (kante < this.distanzC[c]) {
  187. this.distanzC[c] = kante;
  188. this.vorgaengerCzuR[c] = r;
  189. }
  190. }
  191. }
  192. }
  193. let endknotenC = -1;
  194. for (let dijkstraZyklus = 0; dijkstraZyklus < this.cols; dijkstraZyklus++) {
  195. let fromC = 0;
  196. for (let ic = 0; ic < this.cols; ic++) {
  197. let c = this.permC[ic];
  198. if (!this.abgearbeitetC[c]) {
  199. if ((this.abgearbeitetC[fromC]) || (this.distanzC[c] < this.distanzC[fromC])) {
  200. fromC = c;
  201. }
  202. }
  203. }
  204. this.abgearbeitetC[fromC] = true;
  205. let overR = this.c2r[fromC];
  206. if (overR >= 0) {
  207. for (let ic = 0; ic < this.cols; ic++) {
  208. let toC = this.permC[ic];
  209. let kante = this.matrix[overR][toC] + this.potentialR[overR] - this.potentialC[toC];
  210. let distance = this.distanzC[fromC] + kante;
  211. if (distance < this.distanzC[toC]) {
  212. this.distanzC[toC] = distance;
  213. this.vorgaengerCzuR[toC] = overR;
  214. }
  215. }
  216. }
  217. else {
  218. if (endknotenC < 0) {
  219. endknotenC = fromC;
  220. }
  221. }
  222. }
  223. let currentC = endknotenC;
  224. while (currentC >= 0) {
  225. let prevR = this.vorgaengerCzuR[currentC];
  226. let prevC = this.r2c[prevR];
  227. this.r2c[prevR] = currentC;
  228. this.c2r[currentC] = prevR;
  229. currentC = prevC;
  230. }
  231. for (let r = 0; r < this.rows; r++) {
  232. let c = this.r2c[r];
  233. if (c >= 0) {
  234. let kante = this.matrix[r][c] + this.potentialR[r] - this.potentialC[c];
  235. this.potentialR[r] += this.distanzC[c] - kante;
  236. this.potentialC[c] += this.distanzC[c];
  237. }
  238. }
  239. }
  240. return this.r2c;
  241. }
  242. /**
  243. * Interne Methode zum Permutieren oder Initialisieren der Arrays
  244. * {@link KursblockungMatrix#permR} und {@link KursblockungMatrix#permC}.
  245. *
  246. * @param nichtdeterministisch falls {@code true} werden
  247. * {@link KursblockungMatrix#permR} und
  248. * {@link KursblockungMatrix#permC} permutiert,
  249. * sonst initialisiert.
  250. */
  251. initialisierPermRundPermC(nichtdeterministisch) {
  252. if (nichtdeterministisch) {
  253. this.permutiere(this.permR);
  254. this.permutiere(this.permC);
  255. }
  256. else {
  257. KursblockungMatrix.initialisiere(this.permR);
  258. KursblockungMatrix.initialisiere(this.permC);
  259. }
  260. }
  261. /**
  262. * Interne Methode zum Initialisieren eines Arrays so, dass das Array mit den
  263. * Zahlen {@code 0,1,2...} gefüllt wird.
  264. *
  265. * @param perm Das Array, welches mit den Zahlen {@code 0,1,2...} gefüllt wird.
  266. */
  267. static initialisiere(perm) {
  268. let laenge = perm.length;
  269. for (let i = 0; i < laenge; i++) {
  270. perm[i] = i;
  271. }
  272. }
  273. /**
  274. * Interne Methode zum zufälligen Permutieren eines Arrays.
  275. *
  276. * @param perm Das Array, dessen Inhalt zufällig permutiert wird.
  277. */
  278. permutiere(perm) {
  279. let laenge = perm.length;
  280. for (let i = 0; i < laenge; i++) {
  281. let j = this.RND.nextInt(laenge);
  282. let saveI = perm[i];
  283. let saveJ = perm[j];
  284. perm[i] = saveJ;
  285. perm[j] = saveI;
  286. }
  287. }
  288. /**
  289. * Erlaubt Zugriff auf den Inhalt des Arrays.
  290. *
  291. * @return Die Array-Referenz.
  292. */
  293. getMatrix() {
  294. return this.matrix;
  295. }
  296. /**
  297. * Erzeugt String-Ausgabe des Arrays sowie der Zeilen-zu-Spalten-Zuordnung
  298. * {@link KursblockungMatrix#r2c}. Diese Methode ist für Debug-Zwecke gedacht.
  299. *
  300. * @param kommentar Ein Kommentar der über der Matrix angezeigt wird.
  301. * @param zellenbreite Die Breite bei der Ausgabe der Zelle.
  302. * @param mitKnotenPotential Falls {@code true}, werden die Kantenwerte
  303. * umgewichtet entsprechenden der Knotenpotentiale,
  304. * andernfalls bleiben die Kantenwerte unverändert.
  305. *
  306. * @return Eine String-Representation der Matrix.
  307. */
  308. convertToString(kommentar, zellenbreite, mitKnotenPotential) {
  309. let sb = new StringBuilder_1.StringBuilder();
  310. sb.append(kommentar.valueOf() + System_1.System.lineSeparator().valueOf());
  311. for (let r = 0; r < this.rows; r++) {
  312. for (let c = 0; c < this.cols; c++) {
  313. let wert = mitKnotenPotential ? this.matrix[r][c] + this.potentialR[r] - this.potentialC[c] : this.matrix[r][c];
  314. let sWert = "" + wert;
  315. while (sWert.length < zellenbreite)
  316. sWert = " " + sWert.valueOf();
  317. let sZusatz = this.r2c[r] === c ? "*" : " ";
  318. sb.append(sWert.valueOf() + sZusatz.valueOf());
  319. }
  320. sb.append("\n");
  321. }
  322. sb.append("r2c = " + Arrays_1.Arrays.toString(this.r2c).valueOf());
  323. sb.append("\n");
  324. return sb.toString();
  325. }
  326. /**
  327. * Füllt die Matrix mit ganzzahligen zufälligen Zahlenwerten aus dem Intervall
  328. * {@code [von;bis]}.
  329. *
  330. * @param von Der kleinstmögliche zufällige Wert (inklusive).
  331. * @param bis Der größtmögliche zufällige Wert (inklusive).
  332. */
  333. fuelleMitZufallszahlenVonBis(von, bis) {
  334. for (let r = 0; r < this.rows; r++) {
  335. for (let c = 0; c < this.cols; c++) {
  336. this.matrix[r][c] = this.RND.nextInt(bis - von + 1) + von;
  337. }
  338. }
  339. }
  340. isTranspiledInstanceOf(name) {
  341. return ['de.nrw.schule.svws.core.kursblockung.KursblockungMatrix'].includes(name);
  342. }
  343. }
  344. exports.KursblockungMatrix = KursblockungMatrix;
  345. function cast_de_nrw_schule_svws_core_kursblockung_KursblockungMatrix(obj) {
  346. return obj;
  347. }
  348. exports.cast_de_nrw_schule_svws_core_kursblockung_KursblockungMatrix = cast_de_nrw_schule_svws_core_kursblockung_KursblockungMatrix;
  349. //# sourceMappingURL=KursblockungMatrix.js.map