String cserélő algoritmus túl lassú / nem túl hatékony

String cserélő algoritmus túl lassú / nem túl hatékony
2020-11-26T10:54:23+01:00
2021-01-17T20:32:55+01:00
2022-10-15T21:25:37+02:00
aDaM
Hali! Írtam egy saját PHP-s függvényt, melynek feladata egy tömbként megadott szólista szavait (2-max kb. 5 elemű) egy szintén paraméterként megadott sztringben egymásra cserélni. (Tehát ha van egy szó, mely szerepel a sztringben és a tömbben is, az random módon a tömbben szereplő többi szavak valamelyikére cserélődik a sztringben)
A probléma, hogy ha ezres nagyságrendben iterálódik a program ciklusmagja, mely meghívja ezt a függvényt, nagyon belassul, és a PHP kilép a következő hibaüzenettel "( ! ) Fatal error: Maximum execution time of 120 seconds exceeded in fread.php on line 35"
Amely hibaüzenetben a line értéke változik: vagy a fő for ciklus sorát mutatja a függvény kódjában, vagy a két mb_substr() függvényt használó sorokat hibáztatja a lassúságért


A kérdés, hogy hogyan lehetne gyorsabbá tenni a függvényt, ill ha valakinek alapvetően jobb ötlete van a probléma megoldására, az is jöhet.

A $j változós for ciklus azért kell, mert folyamatosan az összes szót vizsgáljuk és bármelyik ha előfordul, cserélve lesz (kivéve az $actL változó által meghatározottat, azt nem cseréli).
A kód:

function replace($inputtext, &$arrCserelni, $actL){ $outtext = ""; $csereCount = count($arrCserelni); //csere tömb elemeinek száma for($i = 0; $i < mb_strlen($inputtext); $i++) { $cserelt = false; for ($j = 0; $j<$csereCount; $j++){ if (!(($actL-1) == $j)){ $strReplace= $arrCserelni[$j]; $lenReplace= mb_strlen($strReplace); $intReplaceWithIndex = randelement($j, $csereCount); $strReplaceWith = '<span style="color: blue; font-weight:bold;">' . $arrCserelni[$intReplaceWithIndex] . '</span>'; $strSubstr = mb_substr($inputtext, $i, $lenReplace); if ($strSubstr == $strReplace){ $outtext .= $strReplaceWith; if (($j == $csereCount-1) or (($actL == $csereCount) and ($j == $csereCount - 2))) $forchar = 1; else $forchar = 0; //utolsó iterációkor eggyel kevesebb legyen a lenReplace $i += $lenReplace - $forchar; $cserelt = true; } else $cserelt = false; } } if ($cserelt == false) $outtext .= mb_substr($inputtext, $i, 1); } return $outtext; }
Mutasd a teljes hozzászólást!

  • Elnézést, rossz bemenetet használtam! Működik úgy néz ki, jól.
    Mutasd a teljes hozzászólást!
  • Hali! Akár a tiéd akár a WittnerIsComing megoldásában hogyan tudok a szövegben éppen cserélt szóra hivatkozni? A függvény kimenetében azt is vissza akarnám majd adni (CSS-elve meg ilyenek, hogy össze lehessen vetni az output szövegben mit mire cserélt. Szóval abban az eredeti szó is szerepelni fog majd az amire cserélt mellett.) Köszi!
    Mutasd a teljes hozzászólást!
  • Hali!

    A preg_replace_callback() függvényben a $m[0] tartalmazza az eredeti szöveget. Semmi nem gátolja, hogy azt is belerakd a cserébe. Például:
    function replaceMod(string $text, array &$exchange, int $actL = -1) { $searchFor = $exchange; if ($actL > 0) { array_splice($searchFor, $actL - 1, 1); } $replacePattern = '/(\((?>[^()]|(?1))*\))(*SKIP)(*FAIL)|([\'\"])[^\2]*?\2(*SKIP)(*FAIL)|'; $replacePattern .= '(?:'.implode('|', array_map('preg_quote', $searchFor)).')/su'; $count = count($exchange); return preg_replace_callback($replacePattern, function($m) use ($count, $exchange) { while (($rep = $exchange[rand(0, $count - 1)]) == $m[0]); return "<span class='word-synonym'>{$rep}<span class='word-orig'>{$m[0]}</span></span>"; }, $text); } $text = 'egy kettő'; $exchange = ['egy', 'kettő']; echo htmlspecialchars(replaceMod($text, $exchange));
    Eredménye:
    <span class='word-synonym'>kettő<span class='word-orig'>egy</span></span> <span class='word-synonym'>egy<span class='word-orig'>kettő</span></span>
    Mutasd a teljes hozzászólást!
  • Köszi! Mondjuk magamtól is rájöhettem volna...

    Kicsit off: a{} karakterekről ezt írja a Google az egyik találatban ill. a php.net-en:

    Accessing characters within string literals using the {} syntax has been deprecated in PHP 7.4. This has been removed in PHP 8.0. Useful functions and operators ...

    Akkor ez így elavult lenne már? Használjak inkább . -okat sztring összefűzéshez? Mit javasoltok?
    Mutasd a teljes hozzászólást!
  • Hali!

    Akkor ez így elavult lenne már? Használjak inkább . -okat sztring összefűzéshez?

    Nem kell mást használnod. Elavult így lenne (lesz majd):
    $name = "aDaM"; $text = "Hello, ".$name{1}."!\n"; echo $text;
    Mivel így tényleg a sztring egy karakterére hivatkozás. Így viszont nem az:
    $name = "aDaM"; $text = "Hello, {$name}!\n"; echo $text;
    Lévén, nem a sztring egyik karakterét jelzi, hanem egy változó tartalmának beillesztését a sztringbe. De alapvetően ekkor sem kellene „egyszerű” változók esetén. Azonban, ha pl. egy objektum egy tulajdonságát szeretnéd beilleszteni a sztringbe, akkor már kell:
    $persons = [ (object) ["name" => "aDaM", "gender" => "male", "age" => 33], (object) ["name" => "EvA", "gender" => "female", "age" => 22], ]; $text = "Hello, {$persons[1]->name}!\n"; echo $text;
    Különben hibára futna (ha az objektumnak nincs toString() metódusa) vagy nem várt eredményre vezetne (értsd: nem azt illesztené be, amit szeretnél). Én megszokásból szoktam használni olyan esetekben is, amikor nem lenne rá szükség.

    Mutasd a teljes hozzászólást!
  • Sajnos van egy kis "gond" az algoritmussal: az egymás mellett álló szavaknál, kifejezéseknél rosszul/csak az egyiket cseréli. Pl.: 

    "A koronavírus-járvány időszakában sokszor..." szövegből "A koronavírus-járvány idején sokszor..." lesz, ahol pedig a " koronavírus-járvány " részsztringet is cserélnie kellene. Mind a kettő kifejezés (a koronavírus-járvány és az időszakában is) szóközzel az elején és végén vannak megadva mint cserélendő kifejezések. (A " koronavírus-járvány " kifejezést  " COVID-19 járvány "-ra kellene konkrétan cserélnie.)

    Szerintem ez azért van, mert a szóközök is meg vannak adva a csere tömbben és a két cserélendő kifejezés közt a szövegben csak egy átfedő/közös szóköz van.
    Ilyenkor egyébként az $m[0] "eredeti kifejezés" is hibás lesz.

    Ugyanez a probléma  WittnerIsComing megoldásával is, ha jól látom.

    Tudom, az én hibám, hogy erre nem hívtam fel a figyelmet már az elején, de nálam is csak most került elő a probléma.

    Ezt kéne valahogy megoldani.

    Köszi szépen! :)
    Mutasd a teljes hozzászólást!
  • Hali!

    Add meg pontosan a hívást, azaz, példaszöveget és a csere-tömböt, amikkel a függvényt hívod. És esetleg a függvény általad jelenleg használt (módosított) változatát is, mivel ötletem nincs, hogy milyen hívással éred el, hogy a „koronavírus-járvány”-t csak „COVID-19 járvány”-ra és ne „idején”-re, illetőleg fordítva, az „időszakában”-t csak „idején”-re és ne „COVID-19 járvány”-ra cserélje – feltéve, hogy egy hívást használsz. Mert ha többször hívod a függvényt ugyanazon (ill. a folyamatosan módosított) sztringen, akkor ilyen probléma nincs – viszont akkor az általad felvázolt sincs.

    Mutasd a teljes hozzászólást!
  • Nem felejtettem el a dolgot, hamarosan bővebben is jelentkezek, most csak ennyi:

    „koronavírus-járvány”-t csak „COVID-19 járvány”-ra és ne „idején”-re, illetőleg fordítva, az „időszakában”-t csak „idején”-re és ne „COVID-19 járvány”-ra cserélje – feltéve, hogy egy hívást használsz.

    Igen, ez esetben két hívás van.

    Lehet az általam eszközölt módosítgatások, kiegészítések lesznek a ludasok, még nézem.

    Egy fájlon/szövegen több hívást is végrehajt a program, attól függően, hány szót vagy kifejezést kell lecserélni.
    Mutasd a teljes hozzászólást!
  • Helló, bocs a késlekedésért!

    Az általad írt függvényt három „üzemmóddal” bővítettem, a felhasználás módjától függően mindegyik más eredményt ad, de mindegyik HTML-be megy ki (egyelőre). A probléma a „color” illetve még inkább a „htmloutbox” móddal van, az eredeti („plain”) mód úgy tűnik, jól működik. Alább lesznek láthatók a módosított függvény és a példáknál a bemásolt eredményeknél a hibák.

    A módosított csere függvény (látszik a két saját $htmlmode paraméter és a három return / „visszatérési mód”):

    function replace(string $text, array &$exchange, int $actL = -1, string $htmlmode) { //Netangel $htmlmode paraméter saját $searchFor = $exchange; if ($actL > 0) { array_splice($searchFor, $actL - 1, 1); } $replacePattern = '/(\((?>[^()]|(?1))*\))(*SKIP)(*FAIL)|(['"])[^\2]*?\2(*SKIP)(*FAIL)|'; $replacePattern .= '(?:'.implode('|', array_map('preg_quote', $searchFor)).')/su'; $count = count($exchange); return preg_replace_callback($replacePattern, function($m) use ($count, $exchange, $htmlmode) {//$htmlmode saját while (($rep = $exchange[rand(0, $count - 1)]) == $m[0]); //if ($htmlmode == "htmloutbox") return "<b><span title='{$m[0]}'>{$rep}</span></b>"; if ($htmlmode == "plain") return $rep; if ($htmlmode == "color") return "<span style='color: blue;'>{$rep}</span>"; if ($htmlmode == "htmloutbox") return "<span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'>".$rep . "</span><span class='txtOriginal' onclick='otherSpan(this);' style='display: inline'>". $m[0] . "</span></span>"; }, $text); }
    (Ez a függvény a „fő csere” függvény által hívódik meg szótár soronként / szinonima tömbönként (később kifejtem).)

    A bemeneti szövegpélda:

    A koronavírus-járvány következtében jelentősen megváltoztak társadalmunkban a szociális érintkezés szokásai.
    A koronavírus-járvány időszakában sokszor használatos kifejezés, a social distancing rövid idő alatt aggódással töltheti el a hazánkban élő lakosság körében személyközi kapcsolatait hosszú évek óta vizsgáló szakembereket.

    Plain mód eredménye - itt még minden jónak tűnik:

    A COVID-19 járvány következtében jelentősen megváltoztak társadalmunkban a szociális kontaktus szokásai.
    A COVID-19 járvány idején sokszor alkalmazott kifejezés, a social distancing (a lakosság egymás közötti érintkezésének radikális csökkentése) rövid idő alatt aggodalommal töltheti el a magyar társadalomban élők személyközi kapcsolatait évtizedek óta vizsgáló szakembereket.

    Color mód eredménye. Itt a hiba: a koronavírus-járvány szót a második bekezdésben már nem cseréli valamiért (az itt félkövér eredetileg kék színnel van kiemelve):

    A COVID-19 járvány következtében jelentősen megváltoztak társadalmunkban a szociális kontaktus szokásai.
    A koronavírus-járvány idején sokszor alkalmazott kifejezés, a social distancing (a lakosság egymás közötti érintkezésének radikális csökkentése) rövid idő alatt aggodalommal töltheti el a magyar társadalomban élők személyközi kapcsolatait évtizedek óta vizsgáló szakembereket.

    Color mód eredményének a forráskódja:

    A<span style='color: blue;'> COVID-19 járvány </span>következtében jelentősen megváltoztak társadalmunkban a szociális<span style='color: blue;'> kontaktus </span>szokásai. A koronavírus-járvány<span style='color: blue;'> idején </span>sokszor<span style='color: blue;'> alkalmazott </span>kifejezés, a<span style='color: blue;'> social distancing (a lakosság egymás közötti érintkezésének radikális csökkentése) </span>rövid idő alatt<span style='color: blue;'> aggodalommal </span>töltheti el a<span style='color: blue;'> magyar társadalomban élők </span>személyközi kapcsolatait<span style='color: blue;'> évtizedek óta </span>vizsgáló szakembereket.

    Htmloutbox mód (a böngészőben látható) eredménye, kérésre ennek a kimeneti forrását is be tudom linkelni (eredetileg a félköver (cserélt) kék színű, az áthúzott (eredeti) pedig szürke):

    COVID-19 járvány koronavírus-járvány következtében jelentősen megváltoztak társadalmunkban a szociális kontaktus érintkezés szokásai.
    A koronavírus-járvány idején időintervallum időszakában sokszor alkalmazott használatos kifejezés, a social distancing (a lakosság egymás közötti érintkezésének radikális csökkentése) social distancing rövid idő alatt aggodalommal aggódással töltheti el a magyar társadalomban élők Magyarországon hazánkban élő népesség lakosság körében személyközi kapcsolatait évtizedek óta hosszú évek óta vizsgáló szakembereket.

    Itt a hiba, hogy nem csak nem cseréli a második bekezdés elején a koronavírus-járvány kifejezést, hanem pl. az időintervallum szót már fölöslegesen teszi oda. Itt a második bekezdésben is a „hazánkban élő” eredeti kifejezés elé is több szinonimát tesz.
    Ebben a módban szándékosan mind az eredeti, mind a csere kifejezést megtartanám, hogy vissza lehessen nézni az egyes kifejezéseket.
     

    A szótár tartalma (amin a cserét hívó rutin végig megy, soronként hívja a replace-et):

    aggodalommal ; aggódással hosszú évek óta ; évtizedek óta alkalmazott ; használatos idején ; időszakában kontaktus ; érintkezés COVID-19 járvány ; koronavírus-járvány social distancing ;Ł social distancing (a lakosság egymás közötti érintkezésének radikális csökkentése) hazánkban élő lakosság körében ; magyar társadalomban élők Magyarországon ; hazánkban lakosság ; népesség időszak; időintervallum
    A cserét hívó függvény lényege, hogy soronként végig megy a szótár fájlon és minden sorban az adott szinonimák alapján (egy sorban vannak az egymásra cserélendő szinonimák), azokat tömbbé alakítva hívja meg a csere függvényt (kivéve ha az adott sor szinonimái nem szerepelnek a bemeneti szövegben).
    Minden sorban egy adott jelentéshez kapcsolódó szinonimák vannak.
    Ezt a „főcsere” függvényt is be tudom idézni, ha kell.
    A „főcsere” függvényt a három különböző mód mindegyikéhez újra hívom meg (tehát háromszor) és így három egymástól független eredményt is ad.

    Remélem ki lehet ezekből hámozni a lényeget.
    Előre is kösz!
    Mutasd a teljes hozzászólást!
  • Hali ismét!

    Közben úgy tűnik, sikerült megoldani a "color" mód problémáját (két szóköz beiktatásával):

    if ($htmlmode == "color") return " <span style='color: blue;'>{$rep}</span> ";
    Illetve a "htmloutbox" problémáját is részben, ugyanígy szóközökkel:

    if ($htmlmode == "htmloutbox") return " <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'>".$rep . "</span><span class='txtOriginal' onclick='otherSpan(this);' style='display: none'>". $m[0] . "</span></span> ";
    Csak ilyenkor a régi kifejezések félbevágva/rosszul jelennek meg (ha rákattint az illető a cserélt kifejezésre - ilyenkor JS segítségével a régi kifejezés lenne látható az új helyett, majd vissza).
    Agyalok rajta, hogy miért lehet -  bármilyen ötlet vagy segítség jöhet.

    A htmloutbox kimeneti kódja (remélem jó lett):

    A <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> COVID-19 járvány </span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> koronavírus-járvány </span></span> következtében jelentősen megváltoztak társadalmunkban a szociális <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> kontaktus </span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> érintkezés </span></span> szokásai. A <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> COVID-19 járvány </span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> koronavírus-járvány </span></span> <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> idején </span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> időintervallum</span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> időszak</span></span> ában </span></span> sokszor <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> alkalmazott </span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> használatos </span></span> kifejezés, a <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> social distancing (a lakosság egymás közötti érintkezésének radikális csökkentése) </span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> social distancing </span></span> rövid idő alatt <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> aggodalommal </span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> aggódással </span></span> töltheti el a <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> magyar társadalomban élők </span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> Magyarországon </span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> hazánkban </span></span> élő <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> népesség </span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> lakosság </span></span> körében </span></span> személyközi kapcsolatait <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'> évtizedek óta </span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'> hosszú évek óta </span></span> vizsgáló szakembereket.
    Köszi!
    Mutasd a teljes hozzászólást!
  • Helló ismét! 

    Úgy néz ki, szűkül a hibalehetőségek köre: a "htmloutbox" módban a replace függvény újra hívásakor bele cserél a már eddig lecserélt, <span> tagek közé tett  eredeti és cserélt kifejezésekbe a szövegben.
    Szerintem ezt kellene valahogy kiszűrni.
    Úgy próbálkoztam eddig, hogy zárójelek közé tettem a <span> tagek közötti akár eredeti, akár csere kifejezést (a függvényben), de ez a kimenetben is látszódik, zárójelek nélkül kellene megoldani.

    Ahogy jelenleg próbálkozom kód:

    if ($htmlmode == "htmloutbox") return " <span><span class='txtReplaced' onclick='otherSpan(this);' style='display: inline'>(". trim($rep) . ")</span> <span class='txtOriginal' onclick='otherSpan(this);' style='display: none'>(". trim($m[0]) . ")</span></span> ";
    Most ennek a módnak a problémája a legfontosabb, szóval ennyi, a többi egyelőre nem kell.

    Köszönöm szépen!
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd