Java - Alternativa za vnořené cykly?
Dobrý den,
Potřeboval bych poradit, jak nahradit vnořené cykly. Řekněme, že budu mít příklad níže.
var array = new char[]{'a', 'b', 'c', 'd'};
var list = getVariation(array, 4);
public static ArrayList<String> getVariation(char characters[], int number_of_characters) {
var length = characters.length;
var list = new ArrayList<String>();
switch (number_of_characters) {
case 2 -> {
for (var i = 0; i < length; i += 1) {
for (var j = 0; j < length; j += 1) {
list.add(characters[i] + "" + characters[j]);
}
}
}
case 3 -> {
for (var i = 0; i < length; i += 1) {
for (var j = 0; j < length; j += 1) {
for (var k = 0; k < length; k += 1) {
list.add(characters[i] + "" + characters[j] + "" + characters[k]);
}
}
}
}
case 4 -> {
for (var i = 0; i < length; i += 1) {
for (var j = 0; j < length; j += 1) {
for (var k = 0; k < length; k += 1) {
for (var l = 0; l < length; l += 1) {
list.add(characters[i] + "" + characters[j] + "" + characters[k] + "" + characters[l]);
}
}
}
}
}
return list;
}
Tento kód je už z principu nepřehledný, protože musí pro každé číslo to napsat zvlášť, jsou omezená pouze na 2,3,4 a z hlediska optimalizace je to taky děs.
Dají se, prosím Vás, nějak vnořené cykly nahradit něčím jiným? Už jenom proto, abych tam mohl zadat libovolné číslo a ne pouze 2,3,4.
Děkuji
Dá se použít například rekurze:
Sorry za opožděnou odpověď. Díky, přesně to jsem chtěl.
Avšak, když tam dám lenght na 14, po chvíli to zhavaruje s java.lang.reflect.InvocationTargetException
Jak, prosím Vás, vyřeším tenhle problém? Při počítání pomocí vnořených cyklů tenhle problém nenastal.
Děkuji
A to ti padá přesně na tomhle kódu, nebo sis to ještě nějak sám upravil?
Ano, jen jsem pochopitelně navýšil počet znaků (počet písmen pořád 4, ale délku jsem nastavil na 14)
Ale jde hlavně o to, že pokud jsem vytvořil několik vnořených cyklů (viz než 4), tak to crashlo kvůli nedostatku paměti, k této chybě nikdy nedošlo.
Toto je její úplné znění
a co se ti nezda? Dosla pamet (heap), protoze jsi vytvoril shitload String instanci…
Ano, ale u vnořených cyklů šlo vždy jenom o překročení paměti, žádnou InvocationTargetException to nehlásilo. A právě na to se ptám, jestli to má řešení?
Když si ten chybový callstack přečteš celý, tak zjistiš, že je to taky způsobené nedostatkem paměti.
Ano, ale jak jsem řekl, u vnořených cyklů docházelo POUZE k nedostatku paměti, žádnou InvocationTargetException to nehlásilo. Zajímá mě význam této chyby, který očividně s rekurzí souvisí, protože u vnořených cyklů vždy pouze přetekla paměť.
Problém bude v kódu, který jsi neuvedl. Mám silné tušení, že se ty, dva případy liší víc, než jenom způsobem vytváření variací.
Samotný kód metody jsem nijak neměnil. A jinak je tam pouze jiná inicializace pole a počet předávám přímo pomocí čísla ne proměnné, ale ani jedno z toho ve výsledku nemá žádný vliv.
Chtěl bych opětovně otevřít toto téma, na které jsem zanevřel a teď jsem se k tomu opětovně vrátil. Jak už bylo zmíněno, náhrada za vnořené cykly je rekurze, kromě kódu v odpovědi, jsem našel různé způsoby, např. tento
Tento kód jednoduše zavolám příkazem
Zmíněnou chybu java.lang.reflect.InvocationTargetException bych odsunul stranou. Tu zavinila knihovna OpenJavaFX, se kterou je více potíží než užitku (od té doby, co není součástí Java) a po odebrání chyba zmizela.
Avšak, stále tu zůstává problém s nedostatkem paměti. Pokud zavolám kód výše např. tímto příkazem
Program poběží cca 3 minuty a pak zhavaruje s chybou Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Problém přetrvává i potom, co jsem NetBeans navýšil paměť na 5 GB (před tím to zhavarovalo mnohem dříve)
Nevíte, prosím Vás, jak to vyřešit? Samozřejmě je tu ještě problém s nedostatkem kapacity pole, ale to jsem chtěl jednoduše vyřešit použitím více polí.
Nicméně, to bych první musel vyřešit problém s pamětí. Popravdě se mně ani nepodařilo zjistit, jak velké pole vlastně je, když to zhavaruje.
Děkuji za odpověď.
No tak si to spočítáme. Máš tam 16 znaků, děláš variace s opakováním o délce 7. Takže celkem 16^7 = 268435456 variací. Každá z těch variací je 7 znaků dlouhá, znak zabírá v paměti 2 byty. Takže celkem 3758096384 B = 3,5 GB. Ve skutečnosti to bude víc, protože instance stringu nejsou jenom samotné znaky.
Problém je ale i v tom, jak ty variace vytváříš:
Vytvoří nový string, do kterého nakopíruje původní a k tomu ten jeden znak. Najednou máš v paměti ten string prakticky dvakrát, než to uklidí garbage collector. A pokud je vytváříš rychleji, než se stihne uklízet, tak ti dojde paměť.
No, popravdě to jediný kód. Ten, co jsem zmínil, se mně líbil právě v předání těch znaků v jednom řetězci, avšak existuje ještě tento
A pak ještě tento
Který kód je ale nejoptimálnější, to nevím. To bych musel napsat JUnit test. Nicméně, momentálně to není podstatné, protože vždy to zhavaruje stejnou chybou.
V tom případě by stačilo použít System.gc();. Otázkou je, kam ho přesně dát a jestli by to pomohlo.
Pak by se jeste mohl podivat, co dela metoda ensureCapacity na ArrayListu...
Určitě ano. Ale tady bych už to viděl jako ten nejmenší problém.
No to je dost velky problem, protoze se ten list musi prekopirovat, ale da se vyresit nastavenim pocatecni kapacity.
Asi bych ty variace neukládal (k čemu je vlastně potřebuješ ???), ale každou hned "použil". Nějak mi uniká, k čemu to může být dobré ... Jinak, nemá smysl řešit něco, co vlastně k ničemu není aneb udělal bych to jinak.
To máte asi pravdu, přímým použitím by paměť nepřetekla. Jenže napsat kód, který první ty variace vytvoří do pole a až potom je využije, je mnohem přehlednější, než když je budu používat hned při vytváření.
Uloz si je do souboru, ne do pameti a pak je cti rovnou ze souboru. Velikost hedne variace znas, tak muzes rovnou precist tu n-tou, ktera te zajima
To je velmi zajímavá myšlenka, která mě upřímně nenapadla, jenže vůbec netuším, jak ten kód upravit.
Místo přidání do listu do zapíšeš do souboru.
Ale spíš bys měl zodpovědět otázku, proč ty variace chceš mít vygenerované. Mám takové tušení, že je vůbec nepotřebuješ.
Ukladani do souboru combinations.txt. A nepotrebujes ani zadne gigabajty heapu. Jako mustr jsem pouzil jeden z tvych kodu, co jsi jsem dal. Ten ktery se mi zdal nejlepsi...
Mala chybicka, zapomnel jsem zapsat posledni davku kombinaci. Oprava:
Ted uz budou v souboru vsechny kombinace...
Proč ??? Prostě ve vhodnou chvíli zavoláš funkci ... a ta funkce může být "uvnitř" libovolně složitá.
Ale pořád jsi neodpověděl, k čemu je vlastně potřebuješ. Třeba se právě v tom skrývá odpověď, jak to celé dělat mnohem lépe.