Anzahl Teiler einer natürlichen Zahl ermitteln

  • Hallo ihr Lieben, ich habe ein etwas mathematisches Problem für euch. Ich versuche die Anzahl aller möglichen Teiler für eine natürliche Zahl zu finden. Dazu zerlege ich eine beliebige Zahl in ihre Primfaktoren, daraus kann man die Teiler bilden. Z.B. für die Zahl 360 sind die Primfaktoren 2^3 * 3^2 * 5. Die Teiler kann man nun durch geschicktes kombinieren der Primfaktoren ermitteln. Hier eine Liste aller Teiler für die Zahl 360:

    Zusätzlich muss noch die 1 als Teiler beachtet werden, insgesamt existieren also 21 mögliche Teiler für die Zahl 360. Die Frage ist jedoch nun, wie man das am besten ausrechnet? Man benötigt im Grunde ja nur die Anzahl der Primfaktoren (3) und die Anzahl der Exponenten jedes Primfaktor (3, 2 und 1). Folgenden Ansatz habe ich bisher, jedoch ist die Funktion _GetComb() falsch da ich nicht richtig rechne. Diese Funktion soll mir letztendlich nur die Anzahl der Teiler zurückgeben, welche das sind ist letztendlich für meine Zwecke egal. Hier der Sourcecode:

    [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit][autoit][/autoit][autoit]

    #include <Array.au3>

    [/autoit][autoit][/autoit][autoit]

    Global $aiNumFact = _GetNumbPrimeFact(360)
    _ArrayDisplay($aiNumFact) ; Anzahl der Exponenten ist korrekt! 3, 2 und 1...

    [/autoit][autoit][/autoit][autoit]

    ; Hier müsste eigentlich 21 heraus kommen... :/
    ConsoleWrite(_GetComb($aiNumFact) & @CRLF)

    [/autoit][autoit][/autoit][autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit][autoit][/autoit][autoit]

    Func _GetNumbPrimeFact($iNumb) ; Exponenten der Primfaktoren bestimmen
    Local $aiFact[Int(Sqrt($iNumb))]
    Local $iDiv = 3
    Local $iCount, $fSqrt

    [/autoit][autoit][/autoit][autoit]

    If Not Mod($iNumb, 2) Then
    While Not Mod($iNumb, 2)
    $aiFact[0] += 1
    $iNumb /= 2
    WEnd
    $iCount = 1
    EndIf

    [/autoit][autoit][/autoit][autoit]

    $fSqrt = Sqrt($iNumb)
    While $iNumb <> 1 And $iDiv <= $fSqrt
    If Not Mod($iNumb, $iDiv) Then
    $aiFact[$iCount] = 1
    $iNumb /= $iDiv
    While Not Mod($iNumb, $iDiv)
    $aiFact[$iCount] += 1
    $iNumb /= $iDiv
    WEnd
    $fSqrt = Sqrt($iNumb)
    $iCount += 1
    EndIf
    $iDiv += 2
    WEnd

    [/autoit][autoit][/autoit][autoit]

    If $iNumb <> 1 Then
    $aiFact[$iCount] = 1
    $iCount += 1
    EndIf

    [/autoit][autoit][/autoit][autoit]

    ReDim $aiFact[$iCount]
    Return $aiFact
    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    ; Diese Funktion macht mir Probleme und Kopfschmerzen...
    Func _GetComb(ByRef $aiFact) ; Anzahl der möglichen Teiler
    Local $iSize = UBound($aiFact)
    Local $iFac = $aiFact[0]
    Local $iAdd = $aiFact[0]
    Local $iC, $iSum, $iAdd

    [/autoit][autoit][/autoit][autoit]

    For $iC = 2 To $iSize
    $iSum += _C($iSize, $iC)
    $iFac *= $aiFact[$iC -1]
    $iAdd += $aiFact[$iC -1]
    Next

    [/autoit][autoit][/autoit][autoit]

    Return $iSum * $iFac + $iAdd +1
    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func _C($n, $k) ; Kombination
    Return _P($n) / (_P($n - $k) * _P($k))
    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func _P($n) ; Permutation
    Local $iRet = 1

    [/autoit][autoit][/autoit][autoit]

    While $n > 1
    $iRet *= $n
    $n -= 1
    WEnd

    [/autoit][autoit][/autoit][autoit]

    Return $iRet
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit]

    Ich hoffe jemand kann mir helfen. Das muss doch mit Stochastik irgendwie zu berechnen sein. Irgendwas mit Permutation, Kombination und/oder Variation muss doch passen damit ich das richtige Ergebnis heraus bekomme. Weiß jemand Rat? :/

    • Offizieller Beitrag

    Hallo,

    es gibt noch 3 Teiler, die hast du vergessen!

    Code
    2^1 * 3^2 = 18
    2^2 * 3^2 = 36
    2^3 * 3^2 = 72


    Für die Zahl 360 gibt es also 24 Teiler.

    Probiers mal so:


    Edit: Mir ist gerade aufgefallen das ab 4 Primfaktoren so auch nicht mehr funktioniert!

  • Sorry bernd670,

    1,2,3,4,5,6,8,9,10,12,15,18,20,24,30,36,40,45,60,72,90,120,180,360

    siehe hierzu:
    http://www.dieter-heidorn.de/Mathematik/VS/…rVielfache.html

    http://www.mathepower.com/teilermenge.php

    Gruß

    Peter

    Hinweise auf Suchmaschinen finde ich überflüssig - wer fragt hat es nicht gefunden oder nicht verstanden. Die Antwort gibt sich oftmals schneller als der Hinweis auf Dr. Goggle & Co.

    Ab 19-10-22 ergänzt um:

    Die Welt wird nicht bedroht von den Menschen, die böse sind, sondern von denen, die das Böse zulassen. (Albert Einstein)

    Einmal editiert, zuletzt von Peter S. Taler (10. Mai 2015 um 12:19)

    • Offizieller Beitrag

    Sind doch 24 oder?

  • Ach Danke, die 3 Kombinationen habe ich tatsächlich vergessen. Also sind es demnach 24 Teiler die die Zahl 360 hat.

    @Peter:
    Leider weiß ich nicht was ich mit den Links anfangen soll. z.B. erklärt mathepower.com dass ein mögliches Verfahren ist, alle Zahlen auszuprobieren und zu prüfen ob diese restlos teilbar ist. Ich selber habe ja bereits ein effektiveres Verfahren gefunden, jedoch scheitert es noch an der Umsetzung da ich versuche jede erdenkliche Kombination aus den Primfaktoren zu bestimmen. Sind es 3 verschiedene Primfaktoren können jeweils die 3 Faktoren alleine stehen, in 2er Grüppchen oder in einer 3er Gruppe multipliziert werden. Das sind also schon 7 mögliche Kombinationen die daraus gebildet werden können.

    A*B*C Als Primfaktoren → A, B, C, A*B, A*C, B*C, A*B*C (7 Teiler)

    Nun ist aber noch die Schwierigkeit da, dass diese noch zusätzlich Exponenten besitzen, das Bedeutet dass sich die Anzahl der möglichen Kombinationen erhöht. Jedoch weiß ich nicht wie man dies nun am besten berechnet. :/

    @edit:
    Bevor ich es vergessen bernd. Dein Beispielskript oben funktioniert auch nicht richtig. Ich habe mal die Zahl 2162160 getestet, sind 320 Teiler, die Funktion gibt aber nur 70 aus. Dieses Skript habe ich dazu verwendet:

    [autoit]

    $iNumb = 2162160
    $iCount = 0

    [/autoit][autoit][/autoit][autoit]

    For $i = 1 To $iNumb
    If Not Mod($iNumb, $i) Then $iCount += 1
    Next

    [/autoit][autoit][/autoit][autoit]

    ConsoleWrite($iCount & @CRLF)

    [/autoit]

    Aber so würde ich das halt ungern lösen da es Zeitfressend ist. ^^

  • Servus, ich konnte das Problem jetzt lösen. :)
    Wenn man sich alle Faktoren als eine Menge vorstellt, so beinhaltet der Faktor 2^5 eine Menge von A = {2^1; 2^2; 2^3; 2^4; 2^5}. Ich habe mir diese Vorstellung zu Nutze gemacht indem ich einfach alle möglichen Kombinationen für die Mengen (die Anzahl der Primfaktoren) durchgegangen bin. Danach musste ich nur noch die Anzahl der darin beinhalteten Elemente in der Menge miteinander multiplizieren. So erhielt ich die Möglichen Kombinationen die mit den Elementen in den Mengen zu bilden wahren. Irgendwie so ähnlich wie das kartesische Produkt. ^^

    Hier das Ergebnis:

    [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit][autoit][/autoit][autoit]

    #include <Array.au3>

    [/autoit][autoit][/autoit][autoit]

    Global $aiNumFact = _GetNumbPrimeFact(2162160)
    ConsoleWrite(_GetComb($aiNumFact) & @CRLF)

    [/autoit][autoit][/autoit][autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit][autoit][/autoit][autoit]

    Func _GetNumbPrimeFact($iNumb)
    Local $aiFact[Int(Sqrt($iNumb))]
    Local $iDiv = 3
    Local $iCount, $fSqrt

    [/autoit][autoit][/autoit][autoit]

    If Not Mod($iNumb, 2) Then
    While Not Mod($iNumb, 2)
    $aiFact[0] += 1
    $iNumb /= 2
    WEnd
    $iCount = 1
    EndIf

    [/autoit][autoit][/autoit][autoit]

    $fSqrt = Sqrt($iNumb)
    While $iNumb <> 1 And $iDiv <= $fSqrt
    If Not Mod($iNumb, $iDiv) Then
    $aiFact[$iCount] = 1
    $iNumb /= $iDiv
    While Not Mod($iNumb, $iDiv)
    $aiFact[$iCount] += 1
    $iNumb /= $iDiv
    WEnd
    $fSqrt = Sqrt($iNumb)
    $iCount += 1
    EndIf
    $iDiv += 2
    WEnd

    [/autoit][autoit][/autoit][autoit]

    If $iNumb <> 1 Then
    $aiFact[$iCount] = 1
    $iCount += 1
    EndIf

    [/autoit][autoit][/autoit][autoit]

    ReDim $aiFact[$iCount]
    Return $aiFact
    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func _GetComb(ByRef $aiFact)
    Local $iSize = UBound($aiFact)
    Local $iC, $asComb, $iN, $aiSplit, $iFact, $iR, $iSum

    [/autoit][autoit][/autoit][autoit]

    For $iC = 1 To $iSize
    $asComb = _ArrayCombinations($aiFact, $iC, '|')

    [/autoit][autoit][/autoit][autoit]

    For $iN = 1 To $asComb[0]
    $aiSplit = StringSplit($asComb[$iN], '|')
    $iFact = 1

    [/autoit][autoit][/autoit][autoit]

    For $iR = 1 To $aiSplit[0]
    $iFact *= $aiSplit[$iR]
    Next

    [/autoit][autoit][/autoit][autoit]

    $iSum += $iFact
    Next
    Next

    [/autoit][autoit][/autoit][autoit]

    Return $iSum +1
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit]
    • Offizieller Beitrag

    Hallo,

    ich habe auch noch eine Lösung gefunden!

    Die dürfte etwas schneller sein weil sie eine For-Schleife weniger hat und ohne Array- und String-Funktionen auskommt!

  • Ja, die Funktion ist tatsächlich schneller.
    Aber was ist der Hintergrundgedanke von Zeile 13?
    Irgendwie kann ich das nicht nachvollziehen.

    • Offizieller Beitrag

    Hallo,

    Angenommen deine Funktion _GetNumbPrimeFact gibt ein Array mit 4 Primfaktoren zurück. Bei 4 Primfaktoren gibt es 2^4 - 1 = 15 Möglichkeiten.

    Wenn man sich jetzt mal die Zahlen von 1 - 15 binär anschaut fällt auf das jeder Wert einer Kombinationsmöglichkeit entspricht.
    A, B, C, D stehen für die Werte im Array A = $aiFact[0], B = $aiFact[1], C = $aiFact[2], D = $aiFact[3].

    In Zeile 13 in meiner Funktion schaue ich nur welche Bits bei $iSum gesetzt sind und berechne das Produkt der Werte.
    zum Beispiel bei $iSum = 13: A * C * D und addiere das Ergebnis zu $iSumGesamt.


    Ich hoffe das ist einigermaßen verständlich.

  • Stimmt, jetzt wo du es sagst. Dann ließe sich das aber auch komplett anders berechnen.
    Nehmen wir an N steht für eine natürliche Zahl, p für ein Primfaktor und e für den Exponenten.

    N = p_1 ^ e_1 * p_2 ^ e_2 * p_3 ^ 3_3 * ...

    Demnach sind also die Anzahl der Teiler folgende:
    T(N) = (e_1 +1) * (e_2 +1) * (e_3 +1) * ...

    Also kann man den Code noch minimieren:

    [autoit]

    Func _GetComb(ByRef $aiFact)
    Local $iSize = UBound($aiFact)
    Local $iFact = 1
    Local $iC

    [/autoit][autoit][/autoit][autoit]

    For $iC = 0 To UBound($aiFact) -1
    $iFact *= $aiFact[$iC] +1
    Next

    [/autoit][autoit][/autoit][autoit]

    Return $iFact
    EndFunc

    [/autoit]
    • Offizieller Beitrag

    Doch so einfach! :thumbup:

  • Ach Sorry, ich habe ja ganz vergessen zu erklären wie ich darauf gekommen bin. ^^
    Also, als ich Bernd sein Lösungsvorschlag gesehen habe, habe ich bemerkt dass man das Problem auch grafisch darstellen kann. Daraufhin habe ich es einfach mal mit einen simplen Baumdiagramm versucht. Nehmen wir also mal die Zahl 360 = 2^3 * 3^2 * 5^1 als Beispiel. Entsprechend würde so das Baumdiagramm aussehen:

    Code
    N
           +---------|---------+
          /          |          \
       [2^1]       [2^2]       [2^3]
       /   \       /   \       /   \
    [3^1] [3^2] [3^1] [3^2] [3^1] [3^2]
      |     |     |     |     |     |
    [5^1] [5^1] [5^1] [5^1] [5^1] [5^1]


    Nun kann man die Produktregel anwenden um die Anzahl der möglichen Kombinationen zu ermitteln. In diesem Fall wäre dass 3*2*1 = 6 Kombinationen. Allerdings muss man auch die Kombinationen beachten die sich nur aus zwei oder einem Element zusammensetzen. Nimmt man als Exponenten die Zahl 0 hinzu, so kann man bestimmte Primfaktoren einfach ausblenden. z.B. bei A * B^0 * C = A * B. Deshalb werden die Exponenten in den Rechnungen nochmals um eins erhöht. Daraus folgt für unser Beispiel: (3 +1) * (2 +1) * (1 +1) = 24 Möglichkeiten.

    • Offizieller Beitrag

    Hallo,

    da sieht man mal wieder das man doch einrostet wenn etwas lange nicht macht! 8o

    Ich weiss nicht ob du die Seite kennst, aber da du ja scheinbar auch mathematik gerne magst solltest du die Seite mal anschauen
    https://projecteuler.net/about

    • Offizieller Beitrag

    Nur mal so nebenbei, wie ich das gelöst hätte:

    Spoiler anzeigen
    [autoit][/autoit] [autoit][/autoit] [autoit]

    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $iNumber = 2162160

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $iTimer = TimerInit()
    $iCount = _GetCombCount($iNumber)
    ConsoleWrite('Count: ' & $iCount & ', Time (ms): ' & TimerDiff($iTimer) & @CR)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $iTimer = TimerInit()
    $aDivider = _GetComb($iNumber)
    ConsoleWrite('Count: ' & UBound($aDivider) & ', Time (ms): ' & TimerDiff($iTimer) & @CR)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($aDivider) - 1
    $aDivider[$i] = Number($aDivider[$i])
    Next
    _ArraySort($aDivider)
    _ArrayDisplay($aDivider)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _GetCombCount($iNumber)
    Local $sDivider = '', $iTmp
    For $i = 1 To Sqrt($iNumber)
    If Not Mod($iNumber, $i) Then $iTmp += 2
    Next
    Return $iTmp
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _GetComb($iNumber)
    Local $sDivider = '', $iTmp
    For $i = 1 To Sqrt($iNumber)
    $iTmp = $iNumber / $i
    If Not Mod($iNumber, $i) Then $sDivider &= $i & ';' & $iTmp & ';'
    Next
    Return StringSplit(StringTrimRight($sDivider, 1), ';', 2)
    EndFunc

    [/autoit]
  • nach obiger Schelte trau ich mich kaum... :)
    aber so sollte es auch funktionieren. Ich gebe allerdings zu, dass für den Fall, dass die Zahl eine Primzahl ist, die Verkürzungen nicht funktionieren, da sich kein Teiler > 1 findet außer der Zahl selbst, man müßte also um die Berechnungszeit klein zu halten noch ein Sieb nach Eratosthenes davorschalten. da mag ich heute aber nichtmehr - oder die Primzahlen in einer "Liste nachschlagen".

    Ja mir ist klar, mein Source ist nicht wirklich schön...



    Edit bernd670: Source als Code eingefügt

    Hinweise auf Suchmaschinen finde ich überflüssig - wer fragt hat es nicht gefunden oder nicht verstanden. Die Antwort gibt sich oftmals schneller als der Hinweis auf Dr. Goggle & Co.

    Ab 19-10-22 ergänzt um:

    Die Welt wird nicht bedroht von den Menschen, die böse sind, sondern von denen, die das Böse zulassen. (Albert Einstein)

    2 Mal editiert, zuletzt von bernd670 (11. Mai 2015 um 09:37)