utils/gaussian-generator.js

/**
 * @fileoverview Wiederverwendbarer Normalverteilungs-Generator (Box-Muller).
 *
 * Erzeugt 2D-Datenpunkte für zwei Klassen basierend auf konfigurierbaren
 * Gauß-Parametern (μx, μy, σx, σy, N).  Kann in jedem Modul verwendet
 * werden, das normalverteilte Cluster benötigt.
 *
 * @module gaussian-generator
 * @author Alexander Wolf
 */

'use strict';

/**
 * @typedef {Object} GaussianClusterConfig
 * @property {number} muX   - Mittelwert x
 * @property {number} muY   - Mittelwert y
 * @property {number} sigmaX - Standardabweichung x
 * @property {number} sigmaY - Standardabweichung y
 * @property {number} n      - Anzahl Punkte
 * @property {0|1}   label   - Klassen-Label
 */

/**
 * @typedef {Object} GeneratedPoint
 * @property {number} x
 * @property {number} y
 * @property {0|1}   label
 */

const GaussianGenerator = (() => {

    /**
     * Box-Muller-Transformation: erzeugt zwei standardnormalverteilte Werte.
     * @private
     * @returns {[number, number]}
     */
    function _boxMuller() {
        let u1 = 0;
        let u2 = 0;
        // Vermeidet log(0)
        while (u1 === 0) { u1 = Math.random(); }
        while (u2 === 0) { u2 = Math.random(); }
        const mag = Math.sqrt(-2.0 * Math.log(u1));
        const angle = 2.0 * Math.PI * u2;
        return [mag * Math.cos(angle), mag * Math.sin(angle)];
    }

    /**
     * Erzeugt einen einzelnen normalverteilten Cluster.
     * @param {GaussianClusterConfig} config
     * @returns {GeneratedPoint[]}
     */
    function generateCluster(config) {
        const { muX, muY, sigmaX, sigmaY, n, label } = config;
        /** @type {GeneratedPoint[]} */
        const points = [];

        for (let i = 0; i < n; i++) {
            const [z1, z2] = _boxMuller();
            points.push({
                x: Math.round((muX + z1 * sigmaX) * 100) / 100,
                y: Math.round((muY + z2 * sigmaY) * 100) / 100,
                label,
            });
        }
        return points;
    }

    /**
     * Erzeugt Datenpunkte für zwei Cluster (Klasse 0 + Klasse 1).
     *
     * @param {GaussianClusterConfig} cluster0 - Parameter für Klasse 0
     * @param {GaussianClusterConfig} cluster1 - Parameter für Klasse 1
     * @returns {GeneratedPoint[]} Kombinierte, gemischte Punkteliste
     */
    function generateTwoClusters(cluster0, cluster1) {
        const c0 = generateCluster({ ...cluster0, label: 0 });
        const c1 = generateCluster({ ...cluster1, label: 1 });
        return [...c0, ...c1];
    }

    /**
     * Erzeugt Default-Konfiguration für zwei gut getrennte Cluster.
     * @param {number} [n=10] - Punkte pro Cluster
     * @returns {{ cluster0: GaussianClusterConfig, cluster1: GaussianClusterConfig }}
     */
    function defaultConfig(n) {
        const count = n || 10;
        return {
            cluster0: { muX: 3, muY: 3, sigmaX: 1.0, sigmaY: 1.0, n: count, label: 0 },
            cluster1: { muX: 7, muY: 7, sigmaX: 1.0, sigmaY: 1.0, n: count, label: 1 },
        };
    }

    return {
        generateCluster,
        generateTwoClusters,
        defaultConfig,
    };
})();


// ===== Universeller Export =====
(function (root) {
    root.GaussianGenerator = GaussianGenerator;
})(typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : this);