Denna artikel kommer att dyka in i principerna för algoritmdesign. Om du inte har en aning om vad jag hänvisar till, läs vidare!
När du hör ordet "algoritm" svarar du förmodligen på ett av tre sätt:
Om du är en av de andra två, är den här artikeln för dig.
Algoritmer är inte en speciell typ av operation, nödvändigtvis. De är begreppsmässiga, en uppsättning steg som du tar i kod för att nå ett visst mål.
Algoritmer har vanligtvis definierats i enkla ord som "instruktioner för att slutföra en uppgift". De har också kallats "recept". I Det sociala nätverket, En algoritm är vad Zuckerberg behövde för att göra Facemash-arbete. Om du såg filmen kommer du noga ihåg att se hur det såg ut som en skribent ekvation på ett fönster i Marks sovsal. Men vad har den skribba algebraen att göra med Marks enkla "heta eller inte" -plats?
Algoritmer är verkligen instruktioner. Kanske en mer exakt beskrivning skulle vara att algoritmer är mönster för att göra en uppgift på ett effektivt sätt. Zuckerbergs Facemash var en röstplats för att bestämma någons attraktivitet i förhållande till en hel grupp människor, men användaren skulle bara få alternativ mellan två personer. Mark Zuckerberg behövde en algoritm som bestämde vilka personer som skulle matcha varandra, och hur man värderar en röst i förhållande till den personens tidigare historia och tidigare utmanare. Detta krävde mer intuition än att bara räkna röster för varje person.
Låt oss till exempel säga att du ville skapa en algoritm för att lägga till 1 till ett negativt tal och subtrahera 1 från ett positivt tal och gör ingenting till 0. Du kan göra något så här (i JavaScript-esque pseudokod):
funktion addOrSubtractOne (number) if (number < 0) return number + 1 else if (number < 0) return number - 1 else if (number == 0) return 0;
Du kanske säger till dig själv, "Det är en funktion." Och du har rätt. Algoritmer är inte en speciell typ av operation, nödvändigtvis. De är begreppsmässiga - en uppsättning steg som du tar i kod för att nå ett visst mål.
Så varför är de en stor sak? Klart att lägga till eller subtrahera 1 till ett nummer är en ganska enkel sak att göra.
Men låt oss prata en sekund om att söka. För att söka efter ett nummer i en rad siffror, hur skulle du tänka att göra det? En naiv inställning skulle vara att iterera numret, kontrollera varje nummer mot det du söker. Men det här är inte en effektiv lösning och har ett mycket brett spektrum av möjliga färdigställelsestider, vilket gör det till en ojämn och opålitlig sökmetod när den skalas till stora sökuppsättningar.
funktion naivSök (nål, höstack) för (var i = 0; i < haystack.length; i++) if (haystack[i] == needle) return needle; return false;
Lyckligtvis kan vi göra bättre än detta för sökning.
Det finns inget bättre sätt att bli en bättre algoritm designer än att ha en djup förståelse och uppskattning för algoritmer.
Låt oss säga att din matris har 50 000 poster, och du har brute-force-sökning (det vill säga sök genom att iterera hela arrayen). Den post du söker efter kommer i bästa fall att bli den första posten i 50 000-postrutan. I värsta fallet kommer emellertid algoritmen att ta 50.000 gånger längre att slutföra än i bästa fallet.
Istället skulle du söka med binär sökning. Det här innebär att sortera matrisen (som jag kommer att låta dig lära dig om själv) och sedan dela upp matrisen i halv och kontrollera om söknumret är större eller mindre än halvvägsmarkeringen i matrisen. Om det är större än halvvägs markeringen av en sorterad array, vet vi att den första halvan kan kasseras, eftersom det sökta numret inte är en del av matrisen. Vi kan också klippa ut mycket arbete genom att definiera de yttre gränserna för matrisen och kontrollera om det sökta numret existerar utanför dessa gränser, och i så fall har vi tagit det som skulle ha varit en multipel iterationsoperation och vänd det till en enda iterationsoperation (som i brutal-kraftalgoritmen skulle ha tagit 50 000 operationer).
sortedHaystack = recursiveSort (haystack); funktion bSearch (nål, sorteradHackstack, förstaIteration) if (firstIteration) om (nål> sorteradHaystack.last | n nål < sortedHaystack.first) return false; if (haystack.length == 2) if (needle == haystack[0]) return haystack[0]; else return haystack[1]; if (needle < haystack[haystack.length/2]) bSearch(needle, haystack[0… haystack.length/2 -1], false); else bSearch(needle, haystack[haystack.length/2… haystack.length], false);
Ta den till synes komplicerade karaktären av en enda binär sökalgoritm, och använd den till miljarder möjliga länkar (som söker via Google). Utöver det, låt oss tillämpa ett slags rankningssystem för de länkade sökningarna för att ge en order på svarsidor. Bättre än, tillämpa något slags till synes slumpmässigt "förslag" -system baserat på artificiella intelligens sociala modeller utformade för att identifiera vem du kanske vill lägga till som en vän.
Detta ger oss en mycket tydligare förståelse för varför algoritmer är mer än bara ett fint namn för funktioner. De är snabba och effektiva sätt att göra något som kräver en högre intuitionsnivå än den mest uppenbara lösningen. De kan ta vad som skulle kräva en superdator år att göra och göra det till en uppgift som slutar i sekunder på en mobiltelefon.
För de flesta av oss som utvecklare designar vi inte dagligen abstrakta algoritmer på hög nivå.
Lyckligtvis står vi på axlarna hos utvecklarna som kom före oss, som skrev inbyggda sorteringsfunktioner och låter oss söka strängar för substrängningar med indexOf på ett effektivt sätt.
Men vi handlar dock om våra egna algoritmer. Vi skapar för
loopar och skrivfunktioner varje dag; så hur kan goda algoritmdesign principer informera skrivandet av dessa funktioner?
En av huvudprinciperna för algoritmisk design är att om möjligt bygga din algoritm på ett sådant sätt att inmatningen i sig gör något av arbetet för dig. Om du till exempel vet att din inmatning alltid kommer att vara nummer behöver du inte ha undantag / kontroller för strängar eller tvinga dina värden till nummer. Om du vet att ditt DOM-element är detsamma varje gång i a för
loop i JavaScript, bör du inte fråga om det här elementet i varje iteration. På samma sätt, i din för
loopar, bör du inte använda bekvämlighetsfunktioner med overhead om du kan uppnå samma sak med (närmare) enkla operationer.
// gör inte det här: för (var i = 1000; i> 0; i -) $ ("# foo").bar"); // gör det istället var foo = $ (" # foo "); var s =" "; för (var i = 1000; i> 0; i -) s + ="bar"; foo.append (s);
Om du är en JavaScript-utvecklare (och du använder jQuery) och du inte vet vad ovanstående funktioner gör och hur de är väsentligt olika, är nästa punkt för dig.
I deras finaste är [algoritmer] kloka och effektiva sätt att göra något som kräver en högre intuitionsnivå än den mest uppenbara lösningen.
Det är lätt att tro att det här säger sig själv. Det finns emellertid en skillnad mellan att "veta hur man skriver jQuery" och "understanding jQuery". Förstå dina verktyg betyder att du förstår vad varje kodlinje gör, både omedelbart (funktionens returvärde eller effekten av en metod) och implicit (hur mycket överhead är förknippat med att köra en biblioteksfunktion eller som är den mest effektiva metod för sammanfogning av en sträng). För att skriva bra algoritmer är det viktigt att känna till prestandan hos funktioner på lägre nivå eller verktyg, inte bara namnet på och genomförandet av dem.
Att utforma effektiva algoritmer är ett full-engagemang. Förutom att förstå dina verktyg som en fristående bit, måste du också förstå hur de interagerar med det större systemet till hands. För att till exempel förstå JavaScript i en viss applikation är det viktigt att förstå DOM och prestanda för JavaScript i cross-browser-scenarier, hur tillgängligt minne påverkar reningshastigheter, strukturen på servrar (och deras svar) som du kanske interagerar med, liksom en myriad av andra överväganden som är immateriella, såsom användningsscenarier.
I allmänhet är målet med algoritmdesign att slutföra ett jobb i färre steg. (Det finns några undantag, t.ex. Bcrypt hashing.) När du skriver din kod, ta hänsyn till Allt av de enkla operationer som datorn tar för att nå målet. Här är en enkel checklista för att komma igång på en väg till effektivare algoritmdesign:
Det finns inget bättre sätt att bli en bättre algoritm designer än att ha en djup förståelse och uppskattning för algoritmer.
.sortera()
, med lägre nivåoperationer. Om du inte visste vad en algoritm var i början av den här artikeln, förhoppningsvis, nu har du en mer konkret förståelse för den något elusiva termen. Som professionella utvecklare är det viktigt att vi förstår att koden vi skriver kan analyseras och optimeras och det är viktigt att vi tar sig tid att göra denna analys av vår kods prestanda.
Något roligt algoritm övar problem du har hittat? Kanske en dynamisk programmering "knapsack problem" eller "drunken walk"? Eller kanske du vet om några bästa metoder för rekursion i Ruby som skiljer sig från samma funktioner som implementeras i Python. Dela dem i kommentarerna!