Entwickler-Ecke

Algorithmen, Optimierung und Assembler - Alternierende Quersumme


Marco D. - Sa 18.11.06 14:37
Titel: Alternierende Quersumme
Ich muss die alternierende Quersumme einer Zahl berechnen. Das macht die Routine Foo:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
function Foo(int: integer): integer;
 var i: integer; s: string;
  begin
    result := 0;
    //alle veränderten Stellen durchgehen
    for i := 1 to 5 do
    begin
      if (i mod 2 = 0then
      begin
        s := inttostr(int);
        result := result + strtoint(s[5 - i])
      end
      else begin
        s := inttostr(int);
        result := result - strtoint(s[5 - i])
      end;
    end;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
showmessage(inttostr(Foo(36036)));
end;

Aber es kommt immer am Ende des letzten Durchlaufes der for-Schleife eine Meldung:
Zitat:

---------------------------
Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt Project1.exe ist eine Exception der Klasse EConvertError aufgetreten. Meldung: ''
---------------------------
OK Hilfe
---------------------------

Das kann ich mir nicht erklären. :gruebel:

Mehr über das Verfahren gibts hier [http://de.wikipedia.org/wiki/Quersumme#Alternierende_Quersumme].


Marc. - Sa 18.11.06 14:49

Ein String beginnt immer bei 1.
So nun läuft dein i von 1 bis 5 - letzlich steht da also:

Delphi-Quelltext
1:
result := result - strtoint(s[5 - 5])                    

5 - 5 ist null, daher nicht definiert ;)

grüße marc


Marco D. - Sa 18.11.06 14:52

user profile iconMarc. hat folgendes geschrieben:
Ein String beginnt immer bei 1.
So nun läuft dein i von 1 bis 5 - letzlich steht da also:

Delphi-Quelltext
1:
result := result - strtoint(s[5 - 5])                    

5 - 5 ist null, daher nicht definiert ;)

grüße marc

:autsch: Klar! Also muss die Schleife von 0-4 gehen?


Marco D. - Sa 18.11.06 14:52

Perfekt es klappt!


Marc. - Sa 18.11.06 14:54

Oder du ziehst nicht i von 5 ab, sondern von 6:

Delphi-Quelltext
1:
result := result - strtoint(s[6 - i])                    


6 - 1 = 5
6 - 2 = 4
...
6 - 5 = 1


//edit: kommt zwars selbe raus, allerdings negativ ;)


Marco D. - Sa 18.11.06 14:58

user profile iconMarc. hat folgendes geschrieben:
Oder du ziehst nicht i von 5 ab, sondern von 6:

Delphi-Quelltext
1:
result := result - strtoint(s[6 - i])                    


6 - 1 = 5
6 - 2 = 4
...
6 - 5 = 1


//edit: kommt zwars selbe raus, allerdings negativ ;)


Ich muss das Prinzip nochmal neu überdenken, vielleicht kann man die Schleife noch optimieren. ;)
Kann man das ganze auch lösen, ohne Stringroutinen zu benutzen? Wie könnte man dann z.B. auf die 5 Ziffer zugreifen?

Moderiert von user profile iconAXMD: Beiträge zusammengefasst


Delete - Sa 18.11.06 17:10

yep, mit einen datentyp der die werte auf einen festen platz stellt, z.b. BCD (binary coded decimal). <HTH>


Marco D. - Sa 18.11.06 17:30

user profile iconGrenzgaenger hat folgendes geschrieben:
yep, mit einen datentyp der die werte auf einen festen platz stellt, z.b. BCD (binary coded decimal). <HTH>

Noch nie von gehört. Kannst du mir das mal bitte erklären? :p


Delete - Sa 18.11.06 17:45

hier sind je 2 Ziffern in einem byte codiert im high und low nibble. der processor kennt das format und kann damit normal rechnen. das bcd format wird normal für kaufmännische anwendungen eingesetzt, da hier der prozessor nicht keine rundungsdifferenzen auftreten, im gegendsatz zu fliesskommazahlen. kannst ja mal kurz im netz (z. B. http://de.wikipedia.org/wiki/BCD-Code) lurken unter BCD oder in der delphi hilfe.

alternativ hier einen etwas optimierteren code deiner routine

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
FUNCTION Foo(int: INTEGER): INTEGER;
VAR
 i, l: INTEGER;
 s: string;
BEGIN
 s := IntToStr(int); l := length(s);  result := 0//vorbelegung
 FOR i := 1 TO l DO  //alle veränderten Stellen durchgehen
  IF (i MOD 2 = 0THEN
   result := result - (INTEGER(s[1 + l - i]) - 48)
  ELSE
   result := result + (INTEGER(s[1 + l - i]) - 48);
END;


//Edit: Klammern ergänzt


Marco D. - Sa 18.11.06 17:52


Delphi-Quelltext
1:
Result := Result - INTEGER(s[1 + l - i]) - 48                    

Würdest du mir diese Zeile bitte erklären? Vor allem die 48.
Und warum ermittelst du die Länge von s bevor s die Zahl zugewiesen wird? Was genau ist denn bei diesem Verfahren nun der Vorteil?


Delete - Sa 18.11.06 18:02

hast recht, das mit der längengabe, hat sich 'n fehler beim layouten eingeschlichen, der ist korrigiert.

das mit dem 48 lässt sich aufklären mit 'n blick auf http://www.torsten-horn.de/techdocs/ascii.htm, da ich jetzt einfach die zahl, die im string steht manuell umrechne, damit erspare ich mir den aufwand über den strtoint... das integer sagt dass es sich um kein zeichen sondern um einen integer handelt, wenn ich von dem integer dann 48 abziehe komme ich ganz normal auf den wert der ziffer... das andere ist die indexierung. plus 1, da der string jetzt ab 1 und nicht mehr bei 0 beginnt, ist hier für den index eine stelle hinzuzufügen.

<HTH>


Marco D. - Sa 18.11.06 18:06

user profile iconGrenzgaenger hat folgendes geschrieben:
hast recht, das mit der längengabe, hat sich 'n fehler beim layouten eingeschlichen, der ist korrigiert.

das mit dem 48 lässt sich aufklären mit 'n blick auf http://www.torsten-horn.de/techdocs/ascii.htm, da ich jetzt einfach die zahl, die im string steht manuell umrechne, damit erspare ich mir den aufwand über den strtoint... das integer sagt dass es sich um kein zeichen sondern um einen integer handelt, wenn ich von dem integer dann 48 abziehe komme ich ganz normal auf den wert der ziffer... das andere ist die indexierung. plus 1, da der string jetzt ab 1 und nicht mehr bei 0 beginnt, ist hier für den index eine stelle hinzuzufügen.

<HTH>

Danke, ich schau's mir mal an. ;)


Martok - So 19.11.06 15:47

user profile iconMarco D. hat folgendes geschrieben:
Kann man das ganze auch lösen, ohne Stringroutinen zu benutzen? Wie könnte man dann z.B. auf die 5 Ziffer zugreifen?


Ich weiß nicht, ob dus noch brauchst, aber ich hab hier ne Funktion. Frage ist, ob damit irgendwas schneller wird.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
function GetStelle(X:int64; St:byte):byte;
var f:double;
begin
  f:= X;
  while st>0 do begin
    f:= f / 10;
    dec(st);
  end;
  Result:= trunc(Frac(f)*10);
end;


Meine Testergebnisse:

Quelltext
1:
2:
3:
4:
5:
6:
4. Stelle von 13549586 ist 9
6. Stelle von 13549586 ist 5
15. Stelle von 13549586 ist 0
0. Stelle von 13549586 ist 0
1000000 Durchlaeufe Stelle 8....
196,957007397027 ms


Optimieren könnte man noch, indem man durch 10^St dividiert, statt einzeln. Wirklich schneller ist das aber auch nicht, die 1 Million Durchläufe kommt dann auf 168 ms.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
uses Math;

function GetStelle2(X:int64; St:byte):byte;
var f:double;
begin
  f:= x*Power(10,-st);
  Result:= trunc(Frac(f)*10);
end;


Marco D. - So 19.11.06 15:50

Danke, aber das hat sich erledigt. ;)


wulfskin - So 19.11.06 16:53

user profile iconMartok hat folgendes geschrieben:
4. Stelle von 13549586 ist 9
Mhhhh, ich seh die vier an vierter Stelle....


Martok - So 19.11.06 19:08

Die Stellen werden von hinten gezählt, wie immer ;)

Quelltext
1:
2:
4. Stelle von 13549586 ist 9
              87654321


wulfskin - So 19.11.06 19:12

user profile iconMartok hat folgendes geschrieben:
Die Stellen werden von hinten gezählt, wie immer ;)

Quelltext
1:
2:
4. Stelle von 13549586 ist 9
              87654321
Sorry, da hab ich vor lauter Bäumen den Wald nicht gesehen. Hab in der zweiten Zeile geschaut und da hat es nach meiner Logik gestimmt.

Oh man, ich geh jetzt schlafen. Danke!