Vinnaren utsedd i Citerus programmeringsutmaning på Jfokus!

2011-02-16 — Tobias Hill — 43 kommentar(er)

Vi är glada över det översvallande intresset och härliga engagemanget som visats i vår programmeringstävling. Vi fick in totalt 109 enskilda bidrag i totalt 14 olika språk. Ungefär hälften av dessa passerade vår rättning som godkända bidrag.

Vissa bidrag var oerhört snabba, andra koncisa och alla unika på sitt sätt. Dels den stora mängden godkända bidrag men även varje enskilt bidrags snillrikhet har inte gjort juryns arbete lätt. Men det har varit värt varenda minut då det varit oerhört lärorikt.

Så, låt oss återvända till vad vi i uppgiftsformuleringen beskrev som goda egenskaper hos en lösning. Vi skrev:

Exempel på saker som vi på Citerus brukar uppskatta är "koncis kod som är lätt att läsa och förstå" samt "val av algoritmer och datastrukturer som ger rimlig prestanda för den uppgift som skall lösas".

I det extrema är oftast dessa egenskaper hos kod motstridiga. Exempelvis är det ofta svårt att göra en extremt snabb lösning som också är koncis och lätt att läsa. Så delvis kan man säga att den här tävlingen handlade om att balansera dessa egenskaper mot varandra. Mot bakgrund av det försökte juryn igår kväll ringa in vilken delmängd bidrag som vi tyckte både var tillräckligt koncisa och snabba men också lättlästa. Kvar blev ca 10 bidrag. Efter ytterligare mangling hade vi bara ett fåtal av dessa kvar och här började det bli riktigt svårt. Alla de kvarvarande bidragen hade en del mycket vackra egenskaper, men även små svagheter. Efter en slutlig omröstning stod det klart att vi alla var mest fascinerade av det bidrag som Carin Lidmar hade lämnat in. Algoritmiskt var Carins koncisa lösning ensam i sitt slag. Dessutom var det en av de snabbaste (men inte snabbast). Det som också gjorde oss både förundrade och väckte vårt intresse var att den varken använde sig av en memoizer eller någon form av pivotering men ändå lyckades vara riktigt snabb.

Så här ser Carins lösning ut:

public class CiterusChallenge {
  static String[] substrings = 
    {"TDD", "DDD", "DI", "DO", "OO", "UI", 
     "ANT", "CV", "IOC", "LOC", "SU", "VO"};

  public static void main(String[] args) {
    System.out.println(removeSubstringsToTheRight(
        "VOCDIITEIOCRUDOIANTOCSLOIOCVESTAIOCVOLIOCENTSU", 0));
  }

  public static String removeSubstringsToTheRight(String s, int start) {
    int minimum = s.length();
    String result = s;
    for (String substr : substrings) {
      int i = s.indexOf(substr);
      while (i >= 0) {
        if (i + substr.length() > start) {
          String newString = s.substring(0, i) + 
                s.substring(i + substr.length());
          newString = removeSubstringsToTheRight(newString, i);
          if (newString.length() < minimum) {
            minimum = newString.length();
            result = newString;
          }
        }
        i = s.indexOf(substr, i + 1);
      }
    }
    return result;
  }
}

Citerus kommer att återkomma inom kort med fler iakttagelser vi gjort under tävlingen.


Kommentarer

Staffan Nöteberg

2011-02-23 kl 13:45 CET

JRuby är snabbare än Java? Tja, Magnus Ljadas översatte mitt halvnaiva (med memoisering, men inte pivotering), men lättbegripliga (nåja) tävlingsbidrag -- och kan ni tro... http://bit.ly/hY3G0B

Mvh // Staffan Nöteberg
http://twitter.com/staffannoteberg

Olle S

2011-02-23 kl 10:49 CET

Jag håller med om att det är snyggt att inte behöva en memoizer för att inte återupprepa samma test flera gånger. Men lösningen är en katastrof när det gäller effektivitet (dock hyfsat snabb på enstaka enkla fall):
1. Den prövar alla kombinationer oavsett om den egentligen redan hittat den perfekt lösningen, dvs tomma strängen (pröva min "A" test)
2. Den prövar alltid minst lika många kombinationer som en partitionerande algoritm, men oftast nästan exponentiellt många gånger fler. (kör med Citerus test sträng och sedan dubbla Citerus test sträng, så ser man skillnaden på tids ökningen mellan vinnaren och tex alt1).

Dessa två missar i kombination gör att i alla fall jag inte kan se vinnaren som riktigt värdig. Dock hade jag nog givit den ett omnämnande som intressantaste/koolaste lösning, och hade den bara haft en av ovanstående egenskaper skulle den varit en värdig vinnare,

Tobias Hill, Citerus

2011-02-22 kl 12:58 CET

Vi ser inget dåligt förlorande i den efterdiskussion som pågår. Vi tycker att det är en både väntad och utmärkt debatt. Juryn valde Carins lösning framförallt baserat på tre meriter. Den var koncisare än nästan alla bidrag och tillräckligt snabb för allt vårt testdata. Utöver detta hade algoritmen en egenskap som väckte vår förundran. Utan memoizer lyckades den ändå begränsa sökrymden – något av en ”algoritmisk memoizer” fast utan dess footprint. Detta gör lösningen effektiv i många fall men som många påpekat är det möjligt att konstruera strängar där en memoizer fungerar bättre, eller där den exponentiella naturen i Carins lösare gör den olämplig. Vårt svåraste övervägande var inte prestandan i Carins lösning utan det faktum att tricket gjorde lösningen svårare att förstå. Vi fann dock att de positiva sidorna övervägde och att ingen annan lösning träffade den sweetspot vi eftersökte bättre än Carins.

Olle S

2011-02-22 kl 03:15 CET

Här är lite till indata som är intressant:

Reducers: "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A"
Input: "AAAAAAAAAAAAAAAAAAAA"
Result: ""
Ser enkelt ut men dödar mångas lösningar :-)

Reducers: "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V","W", "X", "Y", "Z"
Input: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Result: ""
Testar i stort sett om man terminerar när man hittat "" (tomma strängen)

Reducers: "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V","W", "X", "Y"
Input: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Result: "Z"
Lite svårare variant på tomma strängen (varför överlåter jag som läxa till läsaren, tips läs min kommentar nedan)

Olle S

2011-02-22 kl 02:46 CET

ABCDEFGHIJKLMNOPQRSTUVWXYZ" och orden "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z

Olle S

2011-02-22 kl 00:17 CET

En kommentar kring komplexiteten på problemet.

Jag är ganska säker på att detta är ett NP-fullständigt problem (dvs mellan polynom och exponentiellt). Dock har jag inte bevisat det (som man oftast gör genom att mappa mot ett annat känt NP-fullständigt problem).

Eller har någon skrivit en fungerande polynomell lösning, jag har i alla fall inte sett någon hittills.

Det är just därför jag anser att "pivoteringen" borde vara ett krav för en vinnande lösning!

Om det är NP-fullständigt måste man pröva alla kombinationer för att veta den optimala lösningen och det skalar inte hur man än skriver sin kod. Så det enda sättet att klara relativt långa strängar / många reduktions ord är att partitionera (pivotera). Jag hoppades få se lite snygga partitionerings algoritmer men alla bara diskuterar meningslöst tok optimerad kod. Jag vet att det går att göra bättre partitioneringar än den jag lämnade in, har haft en i huvudet sen innan JFokus men inte orkat skriva den i kod än.

Eller är mina kunskaper i Algoritmer och Komplexitet utdaterade?

Olle S

2011-02-21 kl 23:49 CET

Måste bara hålla med om att jag inte tycker vinnaren var en speciellt snygg generell lösning. Att utse en vinnare som inte gör någon pivotering (jag kallar det partitionering) är ju bara löjligt.

Och att sedan kalla den en av de snabbare gör det hela ännu löjligare. Min lösning som inte ens var tok optimerad var 100ggr snabbare på det givna exemplet och om man bara dubblar strängen fick jag vänta 195s på vinnaren (min tog då 230 microsekunder).

Ja det var en kompakt lösning men annars mest suckar man. Alternativ 1 var betydligt bättre även om det tar ett tag att sätta sig in i koden (min lösning slog iofs även alt1 med ca 4ggr på det givna exemplet)

Hur som helst tack för en rolig tävling från en dålig förlorare ;-)

Tobias Hill, Citerus

2011-02-21 kl 23:08 CET

Två nya bidrag har idag publicerats:

Alt 4 - Elegant pivotering

Alt 5 - Koncis Groovy

Anders

2011-02-21 kl 08:49 CET

Det glädjer mig iofs att folk kör min "ABC..." sträng, men den är inte konstruerad för sin inneboende svårighet. Många algoritmer klarar den säkert galant. Jag satte ihop den (i huvudet) specifikt utifrån hur vinnaralgoritmen fungerar som ett exempel på varför jag inte tycker den kan kallas en bra generell lösning på problemet (även om den förstås var klart imponerande på sitt sätt).

Ett tips till Citerus om de skulle göra något liknande igen: be varje bidragslämnare ange, säg, tio testdata vardera (inom av er förutbestämda storleksramar) och kör alla bidrag på alla inlämnade testdata och se till att det tar rimlig tid totalt sett. Det största problemet är nog helt enkelt att ni haft för få (och för lätta) indata att köra på, och detta skulle ju lösa det problemet. (Om vi tävlande lämnar in för lättlösta indata så kan vi ju i alla fall inte gnälla över detta sedan...!)

per ullberg

2011-02-20 kl 20:49 CET

Införde optimeringen som jag nämnde tidigare och får dom här tiderna. Bifogar körningar med Old farts kod som benchmark.


Reducers: "TDD", "DDD", "DI", "DO", "OO", "UI", "ANT", "CV", "IOC", "LOC", "SU", "VO"
Input: "VOCDIITEIOCRUDOIANTOCSLOIOCVESTAIOCVOLIOCENTSU"
Result: "CITERUSLOVESTALENT" (18 chars)
Exe time: 0.193 ms (average for 1000 runs)

Reducers: "AB", "BC", "CB", "AD"
Input: "ABCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCD"
Result: "" (0 chars)
Exe time: 0.258 ms (average for 1000 runs)

Reducers: "AC", "BA", "DB", "AO", "US", "IO", "IOS", "M"
Input: "ABACDDAOBETVAAORIMUIOSNIAOOTEAMONMMÅMIUSOSGOUBABABABABABABABABABABABABABABABAB
ABABABABABABABABASUIOSTBUAOSRACAVIUSOADIOSBLAIIOOMSVMUSVAMBACINNUMSAMREDBAB"
Result: "DETVARINTENÅGOTBRAVALAVVINNARE" (30 chars)
Exe time: 0.651 ms (average for 1000 runs)

Reducers: ":U", "ÄU", "TU2", "M2Å", "M)J", "HO", "GÄD", "ÅÅ", "MÄT", "JG"
Input: ":ÄUUIMTUTGÄÅÅDU22PHOONHGÄDOERM2ÄUÅADAV2M2M2ÅÅ00GÅGÄUÄDÅÅNGEMÄTJMÄTGRSFÖRBÄÅÅT
HHOOTU2TRINÄUGJÄUG"
Result: "IMPONERADAV200GÅNGERSFÖRBÄTTRING" (32 chars)
Exe time: 0.054 ms (average for 1000 runs)

Reducers: "TAT", "AA", "TT", "XY"
Input: "XTATATATY"
Result: "" (0 chars)
Exe time: 0.005 ms (average for 1000 runs)



Old Fart:

Reducers: "TDD", "DDD", "DI", "DO", "OO", "UI", "ANT", "CV", "IOC", "LOC", "SU", "VO"
Input: "VOCDIITEIOCRUDOIANTOCSLOIOCVESTAIOCVOLIOCENTSU"
Result: "CITERUSLOVESTALENT" (18 chars)
Exe time: 0.496 ms (average for 1000 runs)

Reducers: "AB", "BC", "CB", "AD"
Input: "ABCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCD"
Result: "" (0 chars)
Exe time: 0.524 ms (average for 1000 runs)

Reducers: "AC", "BA", "DB", "AO", "US", "IO", "IOS", "M"
Input: "ABACDDAOBETVAAORIMUIOSNIAOOTEAMONMMÅMIUSOSGOUBABABABABABABABABABABABABABABABAB
ABABABABABABABABASUIOSTBUAOSRACAVIUSOADIOSBLAIIOOMSVMUSVAMBACINNUMSAMREDBAB"
Result: "DETVARINTENÅGOTBRAVALAVVINNARE" (30 chars)
Exe time: 0.885 ms (average for 1000 runs)

Reducers: ":U", "ÄU", "TU2", "M2Å", "M)J", "HO", "GÄD", "ÅÅ", "MÄT", "JG"
Input: ":ÄUUIMTUTGÄÅÅDU22PHOONHGÄDOERM2ÄUÅADAV2M2M2ÅÅ00GÅGÄUÄDÅÅNGEMÄTJMÄTGRSFÖRBÄÅÅT
HHOOTU2TRINÄUGJÄUG"
Result: "IMPONERADAV200GÅNGERSFÖRBÄTTRING" (32 chars)
Exe time: 0.082 ms (average for 1000 runs)

Reducers: "TAT", "AA", "TT", "XY"
Input: "XTATATATY"
Result: "" (0 chars)
Exe time: 0.008 ms (average for 1000 runs)

anOldFart

2011-02-20 kl 03:04 CET

http://www.codeupload.com/3803

Reducers: "TDD", "DDD", "DI", "DO", "OO", "UI", "ANT", "CV", "IOC", "LOC", "SU", "VO"
Input: "VOCDIITEIOCRUDOIANTOCSLOIOCVESTAIOCVOLIOCENTSU"
Result: "CITERUSLOVESTALENT" (18 chars)
Exe time: 0,1986 ms (average for 1000 runs)

Reducers: "AB", "BC", "CB", "AD"
Input: "ABCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCD"
Result: "" (0 chars)
Exe time: 0,1622 ms (average for 1000 runs)

Reducers: "AC", "BA", "DB", "AO", "US", "IO", "IOS", "M"
Input: "ABACDDAOBETVAAORIMUIOSNIAOOTEAMONMMÅMIUSOSGOUBABABABABABABABABABABABABABABABABAB
ABABABABABABABASUIOSTBUAOSRACAVIUSOADIOSBLAIIOOMSVMUSVAMBACINNUMSAMREDBAB"
Result: "DETVARINTENÅGOTBRAVALAVVINNARE" (30 chars)
Exe time: 0,3663 ms (average for 1000 runs)

Reducers: ":U", "ÄU", "TU2", "M2Å", "M)J", "HO", "GÄD", "ÅÅ", "MÄT", "JG"
Input: ":ÄUUIMTUTGÄÅÅDU22PHOONHGÄDOERM2ÄUÅADAV2M2M2ÅÅ00GÅGÄUÄDÅÅNGEMÄTJMÄTGRSFÖRBÄÅÅTHH
OOTU
2TRINÄUGJÄUG"
Result: "IMPONERADAV200GÅNGERSFÖRBÄTTRING" (32 chars)
Exe time: 0,0236 ms (average for 1000 runs)

Reducers: "TAT", "AA", "TT", "XY"
Input: "XTATATATY"
Result: "" (0 chars)
Exe time: 0,0023 ms (average for 1000 runs)

Anders Erikson

2011-02-19 kl 23:10 CET

Hej,

det skulle vara roligt att se bidrag i andra JVM-språk, gärna något funktionellt sådant...

Oh grattis Carin till segern med ett bidrag som fortfarande förbryllar! :)

/Anders

Per Ullberg

2011-02-19 kl 11:55 CET

Både min och Carins algoritm mådde bra av lite preprocessning:

public String dividedReduce(String str, String[] words) {

HashSet reducibleChars = new HashSet();
for (String word : words) {
for (char c : word.toCharArray()) {
reducibleChars.add(c);
}
}

String result = "";
int n = str.length();
for (int i = 0; i < n; i++) {
char c = str.charAt(i);
if (!reducibleChars.contains(c)) {


// Do your magic
result = result.concat(reduce(str.substring(0, i), words));


result = result.concat(""+c);
str = str.substring(++i, n);
i = 0;
n = str.length();
}
}

return result.concat(reduce(str, words));
}







result.concat(""+c) var det snabbaste sättet jag kunde hitta för att slå ihop en char med en sträng. Nån som vet nåt snabbare sätt?

/Pelle

Brian Al Saadi

2011-02-19 kl 09:52 CET

Jag körde också Anders sträng. Med min lösning tar det 5 ms med Anders data. Men när jag kör med default tävlingsvärdet tar det 500 ms.

Vinnarens lösning tar 20 ms med default tävlingsvärdet men tvingar mig stänga eclipse när jag kör med Anders data!

Jag använde mig av cachning och elimination av likadana noder.

Hur som helst, var det väldigt lärorikt och kul. Tack för det och Grattis Carin.

Tobias Hill, Citerus

2011-02-18 kl 16:51 CET

Vi tycker det är kul att många gillar Carins vinnarbidrag! Det är också kul att den skapar diskussion, vilket den även har gjort hos oss internt.

Nu vill vi lyfta fram ett antal bidrag som var och en, på sitt sätt, briljerade.

http://www.citerus.se/post/257700-alternativ-l-sning-1-snabb-ven-p

http://www.citerus.se/post/257716-alternativ-l-sning-2-graf-och-dijkstra


Dessutom publicerar vi en av våra interna referensimplementationer:

http://www.citerus.se/post/257722-alternativ-l-sning-3-index-i-yttre


Fler snillrika lösningar kommer publiceras inom kort.

Anders

2011-02-18 kl 15:47 CET

Kristofer, ja, det är i princip dynamisk programmering, men personligen föredrar jag rekursion/memoisering, antar det är en smaksak.

Jag började med att lösa specialfallet att avgöra om en sträng kan elimineras (reduceras till tom) eller ej ("wipe" i programmet). Detta problem bryter ner i två fall, antingen är kan man dela strängen i ett prefix och ett suffix som kan elimineras oberoende av varandra, eller så är det så att första och sista tecknet i strängen elimineras sist av allt, och alltså måste matcha första och sista tecknet i en av de korta strängarna ("bracket" i programmet), och "placera ut" tecknen i korta strängen mellan första och sista i strängen som ska elimineras, dessutom på ett sådant sätt att delsträngarna mellan de utplacerade kan elimineras.

Denna del av lösningen har en komplexitet som beror av längden på de korta strängarna, p.g.a. "bracket" fallet. Om de bara har längd 3 så behöver man bara hitta en punkt i strängen (det mittersta tecknet), men har de längd 4 så måste man välja två punkter, etc.

Sedan används denna lösning för att hitta en minimal lösning i fallet att man inte kan eliminera helt. Även här finns två huvudfall, antingen så eliminerar man första tecknet i strängen, eller så är det kvar i svaret. Om man eliminerar det så måste man eliminera något prefix av strängen helt, ett problem vi just löst, och sedan tar man fram en minimal lösning för resten. Om man inte eliminerar det så tar man istället fram en minimal lösning för alla tecken utom det första, plus förstås att man dras med det första tecknet. Man tar minimum av dessa lösningar förstås.

Jesper Hammarbäck, Citerus

2011-02-18 kl 15:12 CET

På Jfokus frågade några av er efter de strängar vi använt för att testköra inkomna bidrag.
Här är några av dessa.

Följande reduceras till "CITERUS" med samma delsträngar som i tävlingen:
UVODIICVCITEOORUDSUIVOTDUIDDDDUUIDOLOLOCCCVIANTSDDD
CICVTDDTEVOCVOVDDDSUSURSUSUTDVOTITDDODOCDDDCVOVODVODIOCIVOUS

En annan klurig sträng vi testkörde fick vi av Peter Backlund.
Med delsträngarna ["TAT", "AA", "TT", "XY"] reduceras den ner till en tom sträng: "XTATATATY"

Kristofer

2011-02-18 kl 15:07 CET

Anders, kan du beskriva lösningen lite närmare?
Det ser vid första anblick ut som en kvadratisk dynamisk programmering-lösning som försöker hitta lösningen för alla delsträngar S[i:j] och kombinera på optimalt sätt.

Snyggt!
Vore intressant att bygga om den iterativt istället för rekursivt och utan memoizing / pivoting för att se hur "ren" den blir då.

Patrik Åkerfeldt, Citerus

2011-02-18 kl 14:32 CET

Kristofer,
Vi har tagit bort Anders kod för att den formaterar så illa i kommentarsfältet. Det kommer ett inlägg med Anders lösning snart, med snyggare formatering.

Kristofer

2011-02-18 kl 14:29 CET

Anders, jag testkörde din lösning på massa genererad slumpad (elak) indata och den verkar klara alla fall galant och snabbt. Jag förstår fortfarande inte hur den fungerar men än så länge är jag i alla fall imponerad!

(Koden verkar försvunnit nu av någon anledning)

Per Ullberg

2011-02-18 kl 12:20 CET

Jonas: Tack :D

Jonas Barkhagen

2011-02-18 kl 11:52 CET

:ÄUUIMTUTGÄÅÅDU22PHOONHGÄDOERM2ÄUÅADAV2M2M2ÅÅ00GÅGÄUÄDÅÅNGEMÄTJMÄTGRSFÖRBÄÅÅTHH
OOTU2TRINÄUGJÄUG

:U, ÄU, TU2, M2Å, M)J, HO, GÄD, ÅÅ, MÄT, JG

Per Ullberg

2011-02-18 kl 11:12 CET

Anders: Jag körde din sträng och bifogar en körning med standardsträngen som benchmark:

Carin smiskar mig med ungefär faktor 5 på standardlösningen men den verkar inte bli klar för strängen du angav medans min algoritm är riktigt snabb där.

Min lösning:

start: 'ABCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCD'
rest: ''
total time: 73ms
-------------------------------------------------------------------
#reductions: 100
reductions/ms: 1.36986301369863



start: 'VOCDIITEIOCRUDOIANTOCSLOIOCVESTAIOCVOLIOCENTSU'
rest: 'CITERUSLOVESTALENT'
total time: 2965ms
-------------------------------------------------------------------
#reductions: 100
reductions/ms: 0.03372681281618887



Carins:

start: 'VOCDIITEIOCRUDOIANTOCSLOIOCVESTAIOCVOLIOCENTSU'
rest: 'CITERUSLOVESTALENT'
total time: 730ms
-------------------------------------------------------------------
#reductions: 100
reductions/ms: 0.136986301369863


:UME:UNJAGHAM)JROÄUMJGÄTJGPTUM2GÄDÅTU2ÄU:UMÄM2TU2ÅT:U2Å:UÅTIMÄTTÅÅMTÄUUMÄT2)M)JJGJU2
MERAÄUTM:UHOÅÅIM)JNGÄHODAÅÅGÄ:M)JUDLM)JG:U:UJGÅÅOGÄM)JM2ÅDRMMÄT)M2ÅJM)JITJGNHOÄUMGÄD
2GÄDÅJÄM)JUGJGUSM)JÅÄUHODENJGM)JMÄTÄUHOÅÅMMÄTÄJTMTU2)JU2GTÄR200GHOJJGGÅÅÅNG:UGÄDÄ
UGÄDJJGÅÅGERSHONAMÄTMÄTBBHOARMÄÄUT:GÄDUE::UM2ÅTUÅÅ2M)J)

":U", "ÄU", "TU2", "M2Å", "M)J", "HO", "GÄD", "ÅÅ", "MÄT", "JG"

Jonas Barkhagen

2011-02-18 kl 09:45 CET

Ursäkta min griniga ton i förra inlägget.
Utan att vara helt säker törs jag ändå säga att polynomiell tid är uteslutet. Min uppfattning om om "bra lösning" var att det skulle klara så långa strängar som möjligt och med så god prestanda som möjligt. Att vinnaren är sådan att man lätt skapar korta strängar som ger låååång exekvering är synd. Mellan tummen och pekfingret är problemet exponentiellt men den vinnande lösningen till och med faktoriell. :-(

Kristofer

2011-02-18 kl 09:38 CET

Som sagt, det är lätt att hitta på indata som är lätta att lösa snabbt med en polynomiell / girig algoritm och långsamt med en exponentiell algoritm, men det säger inget om huruvida den polynomiella lösningen är korrekt för svårare indata. Publicera era polynomiella lösningar istället för att klaga så vi andra också får chansen att hitta på elaka indata till er.

Peter J

2011-02-18 kl 09:36 CET

Jag har provat att göra den otroligt enkla förändringen av Carins kod att lägga till memoization (några rader kod bara) och då är den snabbare än min lösning på båda era exempel. Det räcker för mig och jag önskar jag kommit på samma idé som hon gjorde.

(Fast min verkar vara snabbare på tävlingssträngen, hehe :)

Jonas Barkhagen

2011-02-18 kl 08:49 CET

Anders: Jag har testat ditt exempel. Avbröt den. Sen utlöste jag samma problem med strängar av typen AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBB med A, B som koder. Jag är (som du?) allmänt desillusionerad. Det framstod som en seriös algoritm/programmerings-tävling men i själva verket utbildar vi citerusfolket. :-(

Kristofer

2011-02-18 kl 08:38 CET

Carin: Grym lösning! Jag tror att din restriktion på hur långt tillbaka i strängen man får söka för rekursiva anropa gör att man slipper memoization för att man helt enkelt inte kommer besöka samma delsträngar så många gånger.

En potientiell (mikro)optimering vore att göra: int i = s.indexOf(substr, start - substr.length());
Då slipper man if (i + substr.length() > start) och slipper scanna igenom början av strängen som ändå inte är intressant.

Anders: Jag ser inte riktigt hur du skulle kunna lösa det här i polynomiell tid för elakt indata, men om du postar ditt polynomiella lösningsförslag kan vi åtminstone granska det och verifiera att den hanterar elakt indata.

Som det ser ut nu verkar det mest vara gnäll :)

Anders

2011-02-18 kl 06:28 CET

Jag har funderat lite på hur juryn och jag kan ha så extremt olika uppfattningar om hur en vinnande lösning borde se ut, och vad jag kom fram till var att de förmodligen inte har förstått problemdomänen (vad skulle Eric Evans säga....?) och/eller inte arbetat sig igenom de tekniska konsekvenserna av detta.

Vad är det vi försöker lösa? En programmeringstävling? Åtminstone för mig betyder det att man borde agera som om problemet vi ska programmera är en förenklad form av något riktigt problem. Detta problem är ett slags pussel, tänk något liknande Sudoku eller korsord. Något som är typiskt för pussel, i motsats till många andra problem, är att indata inte kan approximeras som slumpmässig eftersom pussel är konstruerade för att ha en viss svårighetsgrad, och att denna svårighetsgrad kan vara mycket hög (för att tillfredsställa de riktigt drivna konsumenterna av pusslet i fråga, tänk riktigt avancerade korsordslösare). Man kan antagligen ignorera långa indata helt (det finns inte många korsord på, säg 10 kvadratmeter), men man kan absolut inte ignorera svåra indata av ”normal” längd.

OK, så en ”generell lösare för den här typen av problem” borde innebära att värsta tänkbara sträng av ”normal” längd ska lösas på någotsånär rimlig tid. Det behöver inte vara millisekunder, nödvändigtvis, men någotsånär rimligt. Som jag redan påpekat är detta inte fallet för vinnaren (är det någon som kört mitt exempel från mitt tidigare inlägg? Har körningen gått klart ännu? Hur lång tid tog det i så fall?)

Peter J

2011-02-17 kl 21:20 CET

Lite sent, men i alla fall, grattis Carin!

Karin Edström, VD Citerus

2011-02-17 kl 12:11 CET

Inte ett uns baksmälla - utan bara pur glädje känner vi i Citerus' Jfokus-team idag. Fantastiska dagar, och då har vi ändå det roligaste kvar; eftersnacket - som vi hoppas kommer fortsätta länge. Idag måste vi jobba lite grand, men redan imorgon viker vi stora delar av månadens kompetensutvecklingsdag (våra "Citerusdagar") till återblick och reflektion. Vi kommer då ha möjlighet att nysta både i frågorna "Vann rätt låt?", och "Var juryn mutad?", men framför allt kommer vi kunna publicera några andra bidrag som också stack ut ur mängden på fascinerande sätt (vi kommer självfallet prata med respektive bidragslämnare först). Kanske vågar vi oss också på att publicera det bidrag som vann den interna Citerustävligen, som föregick den riktiga tävlingen. Vårt egoistiska mål med denna utmaning var att lära oss av er (kompetensutveckling är heligt hos oss), och lärt oss har vi sannerligen gjort - massor! Tack för det. Som sagt, vi kommer hålla dialogen levande - så titta gärna in då och då under de närmsta veckorna, och lämna ditt bidrag till debatten!

Per Ullberg

2011-02-17 kl 11:45 CET

Jonas: Oj, tydligen jag som stavar illa... (eller kodar illa).

*skäms*

Per Ullberg

2011-02-17 kl 11:40 CET

Oj, konstig kommentaren blev:

Jonas: Menade du inte

ABACDDAOBETVAAORIMUIOSNIAOOTEAMONMMÅMIUSOSGOUBABABABABABABABABABABABABABABABABA
BABABABABABABABASUIOSTBUAOSRACAVIUSOADIOSBLAIIOOMSVMUSVAMBACINNUMSAMREEDBAB

Eller stavar du illa?

Carin: Snygg lösning

Per Ullberg

2011-02-17 kl 11:38 CET

ABACDDAOBETVAAORIMUIOSNIAOOTEAMONMMÅMIUSOSGOUBABABABABABABABABABABABABABABABABA
BABABABABABABABASUIOSTBUAOSRACAVIUSOADIOSBLAIIOOMSVMUSVAMBACINNUMSAMREEDBAB

Jonas Barkhagen

2011-02-17 kl 09:49 CET

ABACDDAOBETVAAORIMUIOSNIAOOTEAMONMMÅMIUSOSGOUBABABABABABABABABABABABABABABABABABA
BABABABABABABASUIOSTBUAOSRACAVIUSOADIOSBLAIIOOMSVMUSVAMBACINNUMSAMREDBAB

AC BA DB AO US IO IOS M

Anders

2011-02-17 kl 07:46 CET

Oups... Jag drar tillbaka min kommentar till Mattias J, jag missförstod helt vad du försökte göra.

Anders

2011-02-17 kl 07:20 CET

Svar till Chistian: visst finns flera lösningar i generella fallet (tag lång sträng "ABC" och korta strängar "AB" och "BC" t.ex.), men som uppgiften är formulerad är det rimligen så att man kan (eller t.o.m. ska) returnera endast en (jag övervägde returnera alla i min lösning, men valde bort det eftersom, tja, YAGNI :-) ).

Svar till Mattias J: Om String.indexOf är rimligt implementerad (vilket jag inte tvivlar på att den är men jag har inte kollat) borde ditt förslag inte ge något alls. Även om den indexOf inte fångar detta är det säkert ytterst marginellt.

Mattias J

2011-02-17 kl 06:44 CET

Imponerande. Medan jag ännu klurade på hur den fungerade tyckte jag man kunde optimerat
int i = s.indexOf(substr);
till
int i = s.indexOf(substr, start);
men inser att däri finns själva nyckeln till lösningen.

Dock borde kostnaden för att först räkna ut maxlängden på delsträngarna i vissa lägen vägas upp av den optimering som då kan göras genom
int i = s.indexOf(substr, start > maxWordLength ? start - maxWordLength : 0);

Christian

2011-02-16 kl 22:20 CET

Grattis till vinsten!
En undran: visst måste det väl vara så att det (i det generella fallet) kan finnas flera olika strängar som är kortast? Lösningen här plockar bara ut en av dem.

Anders

2011-02-16 kl 21:39 CET

Jag inser förstås att jag är en dålig förlorare, men jag kan ändå inte låta bli att påpeka att ni har valt en lösning som är exponentiell i värsta fall fast det fanns lösningar som är polynomiella i värsta fall (förtydligande: nu tänker jag mig antal tecken i den långa strängen som variabel med de korta strängarna fixerade för att göra det enklare; det blir väldigt krångligt att räkna annars).

Kör vinnaren med input:

"ABCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCD" (exakt lika lång som tävlingstexten) och substrängarna:

"AB", "BC", "CB", "AD"

Ser ni vad jag menar...?

Niklas

2011-02-16 kl 19:24 CET

Elegant lösning! Hatten av!

Peter Backlund

2011-02-16 kl 16:13 CET

Elegant, grattis!

anOldFart

2011-02-16 kl 15:37 CET

Snyggt!
Dubbelt så snabb som min "memoizing-optimerade brute force" lösning:
3.7ms jämfört med mina 8ms.


Kommentera artikeln

Din kommentar

Hantering: Publiceras inte. Vi delar aldrig din e-post med tredje part och vi skickar aldrig oönskad reklam.

Karin Edsröm, VD på Citerus

Vi söker konsulter!

Vi vet att man kan förändra världen med hjälp av mjukvara. Vill du vara med?  Bli en av oss →

 

Inspiration via e-post

  Artiklar om framgångsrik mjukvaruutveckling
  Information om nya kurser och seminarier

Du kan enkelt säga upp din prenumeration om du ändrar dig. Vi lämnar aldrig ut e-postadresser till tredje part.
Läs mer om hur vi hanterar personuppgifter.

 

 

 

Om Citerus

Citerus hjälper företag att lyckas med sin mjukvaruutveckling. Vi erbjuder metodinförande, kurser och träning samt systemutveckling och kan dessutom avlasta våra kunder genom ta oss an både delprojekt och hela projektåtaganden. Allt för att de ska kunna hålla en hög innovationstakt och skapa smarta lösningar som ökar deras konkurrenskraft. Citerus kunder har den gemensamma nämnaren att de ser mjukvaruutveckling som affärskritiskt. Läs mer →

monthly
0.5