JSFiddle - React, Tailwind, and code Playground

HTML

<div>
        
            
        <h2>Laske todennäköisyys sille, että pakan permutaatiossa ei ole samoja kortteja peräkkäin</h2>
        <p>Määritä pakan koostumus. Arvo k tarkoittaa kuinka monta korttia samaa arvoa on ja tämän perässä oleva numero (#) tarkoittaa kuinka monta kertaa tuo lukumäärä esiintyy. Esim. tavallinen korttipakka olisi k=4, #=13. Jos pakka on "epätasainen", lisää k-valintoja. Esim jos haluttaisiin selvittää tn että missään kohtaa ei ole vierekkäin herttaa, ruutua eikä ristiä, niin laitetaan k-valinnat (k=1, #=13) ja (k=13, #=3) eli padat ovat jokainen kortti oma yksikkönsä ja näitä on 13 kappaletta kun taas muut maat on kukin yhdistetty 13 kortin luokaksi.</p>
        
        <div>   
            <ul id="kInputList">
            
            
            </ul>
        
        </div>
        <button id="addButt">Lisää k-valinta</button>
        </div>
        
        <button id="calcButt">Laske todennäköisyys</button>
        <div>
            <span id="answer"></span>
        </div>

CSS

#kInputList li {
    margin-top: 10px;
}

button {
    margin-bottom: 20px;
}

#answer {
    background: lightgreen;
    margin: 10px;
}

JavaScript

const sum = a => a.reduce((cum,x)=>cum+BigInt(x), 0n);
const range = (a,b,c=1) => Array(b-a).fill(0).map((_,j)=>a+j*c);

function gcd(a, b) {
  while (b !== 0n) {
    const r = a % b;
    a = b;
    b = r;
  }
  return a;
}
function binomial(n,k){
    let result = 1n;
    for(var i=1; i <= k; i++){
        result *= BigInt(n+1-i);
        result /= BigInt(i);
    }
    return result;
}
function fallingFact(n, k) {
	let result = 1n;
    for(var i=1; i <= k; i++){
        result *= BigInt(n+1-i);
    }
    return result;
}
function fact(n) {
	let result = 1n;
    for(var i=2; i <= n; i++){
        result *= BigInt(i);
    }
    return result;
}


class P {
	constructor(cs) {
  	this.cs = cs.map(c=>BigInt(c));
  }
  mul(q) {
  	return new P(range(0, this.cs.length + q.cs.length-1)
    		.map(m=>sum(range(0, this.cs.length).map(j=> m-j>=0 && m-j<q.cs.length ? this.cs[j]*q.cs[m-j] : 0n ))));
  }
  toString() {
  	return this.cs.map((c,j)=>c.toString()+ ( j>1 ? "x^"+j.toString() : (j==0 ? "" : "x") ) )
    		.join(" + ");
  }
}


const calcProb = (ns) => {
	const ps = range(0, Math.max.apply(Math, ns)+1)
        .map(m=>
  	         new P(range(0, m+1).map(j=>(-1n)**BigInt(m-j)*binomial(m-1, m-j)*fallingFact(m, m-j) ))
        );
  let q = ns.reduce((cum,x)=>cum.mul(ps[x]), new P([1]));
  let numer = q.cs.reduce((cum, c,j)=>cum+c*fact(j), 0n);
    let denom = fact(sum(ns));
    let g = gcd(numer, denom);
  return [numer/g, denom/g];
};


const makeKInput = () => {
    let ret = document.createElement("li");
    ret.innerHTML = `<label>k: <input type="number" min="1" value="1"></label><label>#: <input type="number" min="0" value="1"></label>`;
    return ret;
};

const addKInput = () => {
    document.getElementById("kInputList").appendChild(makeKInput());
};

const getKNums = (el) => {
    let [k, n] = Array.from(el.querySelectorAll("input")).map(y=>+y.value);
    if (typeof k == "number" && typeof n == "number") {
        return [k,n];
    } else {
        return [1,...