Probleemstelling
In het gegeven getal van 1000 cijfers hebben de vier opeenvolgende cijfers met het grootste product het product
.
Bepaal het grootste product van dertien opeenvolgende cijfers in dat getal.
Eerste analyse
Een string van 1000 cijfers bevat
delen van dertien opeenvolgende cijfers. We bekijken elk deel en bewaren het grootste product. Er is voor deze opgave geen aparte wiskundige formule nodig.
We vergelijken een rechtstreekse aanpak met een variant die een deel overslaat zodra het een nul bevat. Een product met een factor nul kan immers nooit groter worden dan een reeds gevonden positief product.
Een stringdeel omzetten
Met slicing halen we dertien tekens uit de string:
deel = getal[begin : begin + 13]
int(deel) zou van bijvoorbeeld "7316" het getal 7316 maken. Wij willen echter
berekenen. Daarom doorlopen we het deel teken per teken:
product = 1
for teken in deel:
product *= int(teken)
Een afzonderlijk teken zoals "7" is aanvankelijk tekst. int(teken) zet het om in het gehele getal 7, waarna het kan worden vermenigvuldigd. Het product begint bij 1, het neutrale element van de vermenigvuldiging.
Vergelijking van de methoden
De eerste methode zet voor ieder deel alle dertien tekens om en vermenigvuldigt ze. De tweede methode gebruikt eerst:
if "0" in deel:
continue
Een deel met een nul wordt dan onmiddellijk overgeslagen. Van de 988 delen bevatten er 731 een nul; in deze invoer kan de tweede methode dus veel werk vermijden. Daar staat tegenover dat de nultest zelf ook tijd kost en dat delen zonder nul tweemaal worden doorlopen.
Om toevallige schommelingen bij zo’n kort programma te beperken, voert het programma elke methode 1.000 keer uit en rapporteert het de gemiddelde uitvoeringstijd. De precieze verhouding is afhankelijk van de computer en Python-versie.
Gemeenschappelijke gegevens
Beide programma’s gebruiken dezelfde variabele getal, die de 1000 cijfers als één aaneengesloten string bevat. In Python worden opeenvolgende strings tussen haakjes automatisch samengevoegd. De volledige invoer hoeft daardoor niet op één onleesbaar lange regel te staan.
getal = (
"73167176531330624919225119674426574742355349194934"
"96983520312774506326239578318016984801869478851843"
"85861560789112949495459501737958331952853208805511"
"12540698747158523863050715693290963295227443043557"
"66896648950445244523161731856403098711121722383113"
"62229893423380308135336276614282806444486645238749"
"30358907296290491560440772390713810515859307960866"
"70172427121883998797908792274921901699720888093776"
"65727333001053367881220235421809751254540594752243"
"52584907711670556013604839586446706324415722155397"
"53697817977846174064955149290862569321978468622482"
"83972241375657056057490261407972968652414535100474"
"82166370484403199890008895243450658541227588666881"
"16427171479924442928230863465674813919123162824586"
"17866458359124566529476545682848912883142607690042"
"24219022671055626321111109370544217506941658960408"
"07198403850962455444362981230987879927244284909188"
"84580156166097919133875499200524063689912560717606"
"05886116467109405077541002256983155200055935729725"
"71636269561882670428252483600823257530420752963450"
)
Programma 1 – Altijd dertien cijfers vermenigvuldigen
Dit eerste programma berekent voor elk van de 988 delen het volledige product, ook wanneer een van de cijfers nul is.
import time
def grootste_product(reeks, lengte):
grootste = 0
for begin in range(len(reeks) - lengte + 1):
deel = reeks[begin : begin + lengte]
product = 1
for teken in deel:
product *= int(teken)
if product > grootste:
grootste = product
return grootste
# Meet 1.000 uitvoeringen en bereken het gemiddelde
starttijd = time.perf_counter()
for _ in range(1_000):
antwoord = grootste_product(getal, 13)
gemiddelde_tijd = (time.perf_counter() - starttijd) / 1_000
print(f"Het grootste product is: {antwoord}")
print(f"Gemiddelde uitvoeringstijd: {gemiddelde_tijd:.8f} seconden")
Programma 2 – Delen met een nul overslaan
Het tweede programma controleert elk deel eerst op een nul. Alleen wanneer er geen nul aanwezig is, worden de dertien cijfers naar gehele getallen omgezet en vermenigvuldigd.
import time
def grootste_product_zonder_nul(reeks, lengte):
grootste = 0
for begin in range(len(reeks) - lengte + 1):
deel = reeks[begin : begin + lengte]
if "0" in deel:
continue
product = 1
for teken in deel:
product *= int(teken)
if product > grootste:
grootste = product
return grootste
# Meet 1.000 uitvoeringen en bereken het gemiddelde
starttijd = time.perf_counter()
for _ in range(1_000):
antwoord = grootste_product_zonder_nul(getal, 13)
gemiddelde_tijd = (time.perf_counter() - starttijd) / 1_000
print(f"Het grootste product is: {antwoord}")
print(f"Gemiddelde uitvoeringstijd: {gemiddelde_tijd:.8f} seconden")
Beide methoden hebben tijdscomplexiteit
voor een reeks van
cijfers en delen van lengte
. Voor de vaste waarde
is dit
. De nultest verbetert niet de theoretische grootteorde, maar kan wel de werkelijke uitvoeringstijd verkorten.
Concrete tijdsvergelijking
Op dezelfde computer en met telkens 1.000 uitvoeringen werden de volgende gemiddelde tijden gemeten:
| Methode | Gemiddelde tijd per uitvoering |
|---|---|
| Altijd dertien cijfers vermenigvuldigen | 0,00116562 seconden |
| Eerst controleren op een nul | 0,00039123 seconden |
De verhouding is
![]()
De methode met nultest was bij deze meting dus ongeveer 2,98 keer zo snel. Anders uitgedrukt: de gemiddelde uitvoeringstijd daalde met ongeveer
![]()
De precieze tijden kunnen bij een volgende uitvoering verschillen door de computer, de Python-versie en andere actieve processen. De vergelijking blijft hier inhoudelijk zinvol omdat beide methoden onder dezelfde omstandigheden en even vaak werden uitgevoerd.
Resultaat
Beide programma’s geven als grootste product
![]()
Besluit
De rechtstreekse methode is het eenvoudigst, maar voert ook veel overbodige omzettingen en vermenigvuldigingen uit. De nultest is een kleine, duidelijke optimalisatie die gebruikmaakt van de eigenschap dat een product met nul altijd nul is. Omdat 731 van de 988 delen een nul bevatten, leverde deze aanpak in de concrete meting een snelheidswinst van ongeveer factor 2,98 op.