AUFGABE: Jeden tag eine Änderung des Bankcodes...

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
PythonRookie9000
User
Beiträge: 2
Registriert: Mittwoch 1. Februar 2017, 17:16

Mittwoch 1. Februar 2017, 17:29

[codebox=pycon file=Unbenannt.txt][/code]

Hey Leute!

Ich bin hier neu und ein Anfänger in Python. Ich versuche es mir selbst bei zu bringen, was etwas hartnäckig ist. Deswegen brauche ich Hilfe bei folgender Aufgabe:

"Ein Bankdirektor stellte auf dem Ziffernschloss zum Tresorraum die Codezahl 1986 ein. Aus Sicherheitsgründen sollte der Code täglich geändert werden. Dazu dachte sich der Direktor folgende Methode aus:

Er bildete die Summe der aktuellen vier Ziffern (am ersten Tag also 24) und fügte die Einerstelle dieser Summe (bei 24 also 4) der Codezahl rechts an, dafür wurde die erste linksstehende Ziffer (am ersten Tag die 1) gestrichen.

Nun wollte der Bankdirektor wissen, ob bei diesem Vorgehen die Zahl 1986 noch einmal als Code auftreten werde. Als ihm dies ein Computerexperte bestätigte, interessierte er sich brennend dafür, wieviele Tage vergehen werden, bis die Codezahl wieder 1986 lautet."

Das ist soweit meine Lösung:

Code: Alles auswählen

Code = "1986"
Tag = 1

while Tag != 3000:
    Summe = str(int(Code[0])+int(Code[1])+int(Code[2])+int(Code[3]))
    Code = Code.replace(Code[0], "")
    Code = str(Code + Summe[1])
    print(Code)
    Tag += 1
    if Code == "1986":
        print(Tag)
        break
Ich habe jetzt einfach mal eine Schleife bis zum Tag 3000 erstmal genommen. Eig. funktioniert alles bis zum 10. Code, welcher dann plötzlich dreistellig ist, obwohl die Summe des Code davor wunderbar aussieht. Naja es wird mir ein IndexError angezeigt(string out of range) auf Zeile 5 hingewiesen.

Ich würde mich freuen, wenn ihr mir ein bisschen helfen könntet, da ich hier überfragt bin.

Viele Grüße!!

PS: Falls jemand Webseiten mit guten Python-Aufgaben für Anfänger kennt, immer rein damit!! :D
Zuletzt geändert von Anonymous am Mittwoch 1. Februar 2017, 17:31, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

Mittwoch 1. Februar 2017, 17:40

@PythonRookie9000: Schau Dir doch einfach mal die Operation an bei der die Zahl dreistellig wird. Was passiert in dem Schritt denn genau? Wie sieht die Zahl vorher aus und welche Ziffern fehlen danach? Das sollte eigentlich offensichtlich sein was da passiert und warum es so nicht funktionieren kann wenn man es mal Schritt für Schritt durchgeht.

Als Endbedingung für die ``while``-Schleife hätte man auch einfach `True` verwenden können, also eine ”Endlosschleife” die ja abgebrochen wird wenn der Code wieder '1986' ist.

Man könnte aber auch eine ``for``-Schleife mit `itertools.count()` verwenden um das manuelle hochzählen des Tags zu vermeiden.

Der `str()`-Aufruf in Zeile 7 ist sinnfrei weil da bereits eine Zeichenkette übergeben wird und der Index 1 der in dieser Zeile verwendet wird ist ein weiterer Programmierfehler. Der stimmt nämlich nur wenn die Summe zweistellig ist. Man kann aber auch vier Ziffern haben die nur eine einstellige Summe haben, was dann zu einem `IndexError` führt.

Namenskonvention ist übrigens das nur Klassennamen mit einem Grossbuchstaben beginnen und Konstanten durchgehend in Grossbuchstaben geschrieben werden.
Sirius3
User
Beiträge: 7781
Registriert: Sonntag 21. Oktober 2012, 17:20

Mittwoch 1. Februar 2017, 17:42

@PythonRookie9000: schau mal in der Dokumentation nach, was Code.replace genau macht und wann das ein Problem werden könnte. Warum könnte Zeile 7 auch ein Problem machen?

Allgemein: Wenn man weiß, wie viele Durchläufe eine while-Schleife laut Schleifenbedingung macht, sollte man eine for-Schleife verwenden. Variablennamen werden komplett klein geschrieben.
PythonRookie9000
User
Beiträge: 2
Registriert: Mittwoch 1. Februar 2017, 17:16

Mittwoch 1. Februar 2017, 20:04

@BlackJack
@Sirius3
Aaaaalllsooo,

Ich habe jetzt anstatt das replace Kommando, einfach code[1:] genommen. Es war echt nicht so einfach das Problem zu erkennen( wegen dem mangelnden Verständnis der Vorgehensweise der Sprache) und dann noch zu lösen. Ich habe die ganze Zeit nach anderen Kommandos gesucht, aber dann war es doch so einfach...

In Linie 7 habe ich anstatt summe[1] , summe[-1] benutzt. Hätte ich gleich sehen müssen... Zu dem str in der Linie 7 sag ich mal lieber nichts mehr...

Und ab heute werden Variablennamen klein geschrieben!!

Vielen Dank für eure schnelle Antwort und dass ihr mir nicht gleich die Lösung verratet habt!

Ich denke mal ihr wollt unbedingt die wissen wie viele Tage es dauert bis der Code wieder 1986 lautet...1561. Unglaublich, ich weiß!
BlackJack

Mittwoch 1. Februar 2017, 23:47

@PythonRookie9000: Dabei hast Du jetzt aber den ersten Tag mitgezählt, ich komme nämlich nur auf 1560 Tage. Mit QBasic:

Code: Alles auswählen

CONST StartCode$="1986"
Day=0
Code$=StartCode$
DO
  Sum=0
  FOR i=1 TO 4
    Sum=Sum+VAL(MID$(Code$,i,1))
  NEXT
  Code$=MID$(Code$,2)+LTRIM$(STR$(Sum MOD 10))
  Day=Day+1
  PRINT "Day:";Day;" Code: ";Code$
LOOP UNTIL Code$=StartCode$
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Donnerstag 2. Februar 2017, 08:39

Ich will auch :lol:
Nicht besonderes, aber zum warm werden bevor man anfängt zu arbeiten.

Code: Alles auswählen

def mathe():
    ur_code = "1986"
    Code = "1986"
    Tag = 0
    while True:
        ergebnis = 0
        for zahl in Code:
            ergebnis += int(zahl)
        Code += str(ergebnis)[-1]
        Code = Code[1:]
        Tag += 1
        if Code == ur_code: 
            break
    print("Es sind %s Tage vergangen, bis sich der Code wiederholt hat!" % Tag)


if __name__ == '__main__':
    mathe()
BlackJack

Donnerstag 2. Februar 2017, 08:56

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
from itertools import count


def main():
    start_code = '1986'
    code = start_code
    for day in count(1):
        code = code[1:] + str(sum(map(int, code)) % 10)
        if code == start_code:
            break
    print('Nach {} Tagen ist der Code wieder {}.'.format(day, start_code))


if __name__ == '__main__':
    main()
BlackJack

Donnerstag 2. Februar 2017, 11:25

@sebastian0202: Wenn Du die Funktion schon `mathe()` nennst, hätte ich ja mindestens erwartet, dass Du keine Zeichenketten sondern ausschliesslich Zahlen und mathematische Operationen verwendest. :P

@PythonRookie9000: Das wäre vielleicht eine interessante weitere Teilaufgabe, den Code als *eine* Zahl zu repräsentieren und nicht auf Zeichenkettenoperationen zurück zu greifen.
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Donnerstag 2. Februar 2017, 16:00

War etwas schwierig und lässt sich sicher auch eleganter lösen.
Aber hier die mathe Funktion nur mit Zahlen.

Code: Alles auswählen

def mathe(ur_code):
    Code = ur_code
    Zahl = ur_code
    Tag = 0
    while True:
        zwischen_ergebnis = 0
        rest_raw = 0
        for potenz in (10,100,1000,10000):
            rest_raw = Zahl % potenz
            rest = Zahl % potenz
            while rest > 9:
                rest /= 10
            zwischen_ergebnis += rest
            Zahl -= rest_raw

        Code -= rest_raw
        Code *= 10
        Code += zwischen_ergebnis % 10
        Zahl = Code
        
        Tag += 1
        if Zahl == ur_code:
            break
    return Tag
 
if __name__ == '__main__':
    code = 1986
    tag = mathe(code)
    print("Es vergehen %i Tage, bis sich der Code %i wiederholt!" % (tag, code))
Sirius3
User
Beiträge: 7781
Registriert: Sonntag 21. Oktober 2012, 17:20

Donnerstag 2. Februar 2017, 16:33

@sebastian0202: bei der Länge der Funktion ist es schon sinnvoll, mehrere Funktionen zu schreiben; z.B. kann man das Bilden der Quersumme gut in eine eigene Funktion packen.

Code: Alles auswählen

from itertools import count
def cross_sum(number, base=10):
    result = 0
    while number:
        number, residual = divmod(number, base)
        result += residual
    return result

def calculate_series_length(code):
    original_code = code
    for cnt in count(1):
        code = (code % 1000) * 10 + cross_sum(code) % 10
        if original_code == code:
            break
    return cnt
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Donnerstag 2. Februar 2017, 16:44

Habe gesehen, dass es Probleme geben kann sobald eine 0 in der Zahl vorkommt.
Muss die Zahl also erst erweitern bevor ich mit ihr rechne.
Dafür darf der Code jetzt beliebig lang sein.

Nachtrag: Die vorige Funktion hatte ja nur 24 Zeilen. Für mich war sie übersichtlich.
Ich konnte mich damit noch nicht anfreunden, für jedes mögliche Problem was sich auslagern lässt, auch auszulagern.
Damit hat man unzählige Funktionen und da verliere ich schnell den Überblick.

Code: Alles auswählen

def stellen(zahl):
    anzahl = 1
    while zahl > 9:
        zahl /= 10
        anzahl += 1
    return anzahl

def code_erweitern(code, stellen):
    zahl = 1
    for i in range(stellen):
        zahl *= 10
    code += zahl
    return code

def rest_potenz(anzahl_stellen, zahl):
    zwischen_ergebnis = 0
    rest = 0
    potenz = 0.1
    for i in range(anzahl_stellen):
        rest = zahl % 10
        zwischen_ergebnis += rest
        zahl -= rest
        zahl /= 10
        potenz *= 10
    return rest, zwischen_ergebnis, potenz

def stelle_verrutschen(zahl, rest, zwischen_ergebnis, potenz):
    zahl -= (rest * potenz) 
    rest = zahl % potenz
    zahl -= rest
    zahl += rest*10
    zahl += zwischen_ergebnis % 10
    return zahl

def mathe(ur_code):
    anzahl_stellen = stellen(ur_code)
    Code = code_erweitern(ur_code, anzahl_stellen)
    Zahl = Code

    Tag = 0
    while True:
        rest, zwischen_ergebnis, potenz = rest_potenz(anzahl_stellen, Zahl)
        Code = stelle_verrutschen(Code, rest, zwischen_ergebnis, potenz)
        Zahl = Code
        Tag += 1
        if Zahl-potenz*10 == ur_code:
            break
    return Tag
 
if __name__ == '__main__':
    code = 1986
    tag = mathe(code)
    print("Es vergehen %i Tage, bis sich der Code %i wiederholt!" % (tag, code))

BlackJack

Donnerstag 2. Februar 2017, 18:02

@sebastian0202: Die Aufteilung auf die Funktionen ist ja auch total komisch. Das sieht irgendwie so willkürlich aus und nicht so als wenn man wirklich sinnvoll Teilprobleme in Funktionen ausgelagert hätte. So etwas wie `quersumme()` was eine Zahl bekommt und eine Zahl zurück gibt. Und `ziffern_verschieben()` das den Code und eine einstellige Zahl bekommt und eine Zahl zurück gibt die um eine Zehnerstelle verschoben ohne die erste Ziffer und mit der neuen Ziffer zurück gibt, also beispielsweise `ziffern_verschieben(4711, 3)` → 7113.
BlackJack

Freitag 3. Februar 2017, 23:43

Das ganze mit Zahlen in Assembler für DOS (129 Bytes .COM-Datei):
[codebox=asm file=Unbenannt.asm] cpu 386
org 0x0100
[map all]

start_code equ 1986

segment .code

; `code` is stored in SI register.
; `sum` is stored in DI register.

start:
xor ax,ax ; day := 0
mov [day],ax
mov si,start_code ; code := start_code

day_loop:
xor ax,ax ; sum := 0
mov di,ax

mov ax,si ; divide code value by 10 until it reaches 0
mov bx,10 ; summing the remainder in `sum`.
sum_loop:
or ax,ax
jz sum_loop_exit
xor dx,dx
div bx
add di,dx
jmp sum_loop
sum_loop_exit:

mov ax,di ; CX = last decimal digit of `sum`
xor dx,dx
div bx
mov cx,dx

mov ax,si ; multiply `code` by 10 and add the digit in CX.
mul bx
add ax,cx

mov bx,10000 ; get rid of digits beyound the four of the code.
div bx
mov si,dx

inc word [day] ; INC(day)

; check if we reached the day with the same code we started with
cmp si,start_code
jne day_loop

mov ax,[day] ; print day and code.
call print_number

mov ah,2
mov dl,' '
int 0x21

mov ax,si
call print_number

mov ah,2
mov dl,13
int 0x21

mov ah,2
mov dl,10
int 0x21

exit:
mov ax,0x4c00 ; AL = exit code.
int 0x21

;--------------------------------------------------------------------
print_number:
xor cx,cx
mov bx,10
digit_loop:
or ax,ax
jz digit_loop_exit
xor dx,dx
div bx
add dl,'0'
push dx
inc cx
jmp digit_loop
digit_loop_exit:
or cx,cx
jnz not_zero_digits
push word '0'
inc cx
not_zero_digits:

print_loop:
mov ah,2
pop dx
int 0x21
loop print_loop

ret

;--------------------------------------------------------------------

segment .bss

day:
resw 1[/code]
bords0
User
Beiträge: 170
Registriert: Mittwoch 4. Juli 2007, 20:40

Freitag 3. Februar 2017, 23:52

Oder kurz, knackig und hässlich:

Code: Alles auswählen

c = 1968
d = c + c // 10 + c // 100 + c // 1000
for i in range(1, 10000):
    c, d = 10 * (c % 1000) + d % 10, d + d % 10 - c // 1000
    if c == 1968:
        break
print(i)
BlackJack

Samstag 4. Februar 2017, 01:00

Die Aufteilung auf Funktionen die ein bisschen weniger unübersichtlich aussehen in CoffeeScript:
[codebox=coffeescript file=Unbenannt.coffee]#!/usr/bin/env coffee
'use strict'

crossSum = (code) ->
sum = 0
while code > 0
digit = code % 10
code = (code - digit) / 10
sum += digit
sum


shiftCode = (code, digit) ->
code * 10 % 10000 + digit


main = ->
startCode = 1986
day = 0
code = startCode
loop
code = shiftCode(code, crossSum(code) % 10)
day++
break if code is startCode
console.log(day, code)


main() if require.main == module[/code]
Antworten