Unbekannte Dezimalzahl als Bruch

  • VB.NET

Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    x und 1 sind in jedem Falle eine Triviallösung.
    Kannst Du mal ein Beispiel machen?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich hatte da mal was gemacht, ist schon etwas länger her, also kann es sein, dass es an manchen Stellen etwas unsauber ist, aber vielleicht kannst du ja was damit anfangen.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' Stellt einen Bruch dar, der sich automatisch kürzt und der die Konvertierung von und in Dezimalzahlen unterstützt.
    3. ''' </summary>
    4. ''' <remarks></remarks>
    5. Public Structure Fraction
    6. Private m_Numerator As Long
    7. Private m_Denominator As Long
    8. Private m_DecimalValue As Decimal
    9. Public ReadOnly Property isZero As Boolean
    10. Get
    11. Return m_Numerator = 0 OrElse m_Denominator = 0
    12. End Get
    13. End Property
    14. ''' <summary>
    15. ''' Der Zähler des Bruches.
    16. ''' </summary>
    17. ''' <value></value>
    18. ''' <returns></returns>
    19. ''' <remarks></remarks>
    20. Public Property numerator As Long
    21. Get
    22. Return m_Numerator
    23. End Get
    24. Set(value As Long)
    25. If value = 0 Then
    26. m_Numerator = 0
    27. m_DecimalValue = 0D
    28. Exit Property
    29. End If
    30. If m_Denominator = 0 Then
    31. m_Numerator = value
    32. Exit Property
    33. End If
    34. Dim gcd As Long = MathUtils.GetGreatestCommonDivisor(value, m_Denominator)
    35. m_Numerator = value \ gcd
    36. m_Denominator = m_Denominator \ gcd
    37. m_DecimalValue = Convert.ToDecimal(m_Numerator) / Convert.ToDecimal(m_Denominator)
    38. End Set
    39. End Property
    40. ''' <summary>
    41. ''' Der Nenner des Bruches.
    42. ''' </summary>
    43. ''' <value></value>
    44. ''' <returns></returns>
    45. ''' <remarks></remarks>
    46. Public Property denominator As Long
    47. Get
    48. Return m_Denominator
    49. End Get
    50. Set(value As Long)
    51. If value = 0 Then
    52. m_Denominator = 0
    53. m_DecimalValue = 0D
    54. Exit Property
    55. End If
    56. If m_Numerator = 0 Then
    57. m_Denominator = value
    58. Exit Property
    59. End If
    60. Dim gcd As Long = MathUtils.GetGreatestCommonDivisor(m_Numerator, value)
    61. m_Numerator = m_Numerator \ gcd
    62. m_Denominator = value \ gcd
    63. m_DecimalValue = Convert.ToDecimal(m_Numerator) / Convert.ToDecimal(m_Denominator)
    64. End Set
    65. End Property
    66. ''' <summary>
    67. ''' Die Darstellung des Bruches als Dezimalzahl.
    68. ''' </summary>
    69. ''' <value></value>
    70. ''' <returns></returns>
    71. ''' <remarks></remarks>
    72. Public Property decimalValue As Decimal
    73. Get
    74. Return m_DecimalValue
    75. End Get
    76. Set(value As Decimal)
    77. If value = 0 Then
    78. m_Numerator = 0
    79. m_Denominator = 0
    80. m_DecimalValue = 0D
    81. Exit Property
    82. End If
    83. Dim movedDecimals As Integer
    84. Dim intValue As BigInteger = getBigInt(value, movedDecimals)
    85. Dim exponent As New BigInteger(10 ^ movedDecimals)
    86. Dim gcd As BigInteger = BigInteger.GreatestCommonDivisor(intValue, exponent)
    87. m_Numerator = Long.Parse((intValue / gcd).ToString)
    88. m_Denominator = Long.Parse((exponent / gcd).ToString)
    89. m_DecimalValue = Convert.ToDecimal(m_Numerator) / Convert.ToDecimal(m_Denominator)
    90. End Set
    91. End Property
    92. Private Function getBigInt(ByVal val As Decimal, ByRef movedDecimals As Integer) As BigInteger
    93. Dim nF As NumberFormatInfo = CultureInfo.InvariantCulture.NumberFormat
    94. Dim s As String = val.ToString(nF).ToLowerInvariant
    95. Dim decSep As String = nF.NumberDecimalSeparator
    96. Dim pot As String = "e+"
    97. If s.Contains(pot) Then
    98. Dim parts() As String = s.Split({pot}, StringSplitOptions.RemoveEmptyEntries)
    99. Dim base As String = parts(0)
    100. Dim exponent As Integer = Integer.Parse(parts(1))
    101. If base.Contains(decSep) Then
    102. Dim i As Integer = base.IndexOf(decSep)
    103. movedDecimals = exponent + (base.Length - (i + decSep.Length))
    104. Return BigInteger.Parse(base.Remove(i, decSep.Length), nF) * exponent
    105. Else
    106. movedDecimals = exponent
    107. Return BigInteger.Parse(base, nF) * exponent
    108. End If
    109. ElseIf s.Contains(decSep) Then
    110. Dim i As Integer = s.IndexOf(decSep)
    111. movedDecimals = s.Length - (i + decSep.Length)
    112. Return BigInteger.Parse(s.Remove(i, decSep.Count), nF)
    113. Else
    114. Return BigInteger.Parse(s, nF)
    115. End If
    116. End Function
    117. ''' <summary>
    118. ''' Erstellt einen neuen Bruch aus Zähler und Nenner.
    119. ''' </summary>
    120. ''' <param name="denom">Der Nenner des Bruches.</param>
    121. ''' <param name="num">Der Zähler des Bruches.</param>
    122. ''' <remarks></remarks>
    123. Sub New(ByVal num As Long, ByVal denom As Long)
    124. If num = 0 OrElse denom = 0 Then
    125. m_Numerator = num
    126. m_Denominator = denom
    127. Exit Sub
    128. End If
    129. Dim gcd As Long = MathUtils.GetGreatestCommonDivisor(num, denom)
    130. m_Denominator = denom \ gcd
    131. m_Numerator = num \ gcd
    132. m_DecimalValue = Convert.ToDecimal(m_Numerator) / Convert.ToDecimal(m_Denominator)
    133. End Sub
    134. ''' <summary>
    135. ''' Erstellt einen neuen Bruch aus einer Dezimalzahl.
    136. ''' </summary>
    137. ''' <param name="decVal"></param>
    138. ''' <remarks></remarks>
    139. Sub New(ByVal decVal As Decimal)
    140. decimalValue = decVal
    141. End Sub
    142. ''' <summary>
    143. ''' Erstellt einen neuen Bruch aus einer Dezimalzahl.
    144. ''' </summary>
    145. ''' <param name="decVal"></param>
    146. ''' <remarks></remarks>
    147. Sub New(ByVal decVal As Double)
    148. decimalValue = Convert.ToDecimal(decVal)
    149. End Sub
    150. ''' <summary>
    151. ''' Erstellt einen neuen Bruch aus einem anderen.
    152. ''' </summary>
    153. ''' <param name="f"></param>
    154. ''' <remarks></remarks>
    155. Sub New(ByVal f As Fraction)
    156. m_Numerator = f.m_Numerator
    157. m_Denominator = f.m_Denominator
    158. m_DecimalValue = f.m_DecimalValue
    159. End Sub
    160. ''' <summary>
    161. ''' Konvertiert diesen Bruch in seine eintsprechende Zeichenfolgendarstellung.
    162. ''' </summary>
    163. ''' <returns></returns>
    164. ''' <remarks></remarks>
    165. Public Overrides Function ToString() As String
    166. Return String.Concat({Me.GetType.FullName, ": ", numerator.ToString, "/", denominator.ToString, " {", decimalValue.ToString, "}"})
    167. End Function
    168. Public Shared Operator +(ByVal v1 As Fraction, ByVal v2 As Double) As Double
    169. Return v1.decimalValue + v2
    170. End Operator
    171. Public Shared Operator +(ByVal v1 As Double, ByVal v2 As Fraction) As Double
    172. Return v1 + v2.decimalValue
    173. End Operator
    174. Public Shared Operator -(ByVal v1 As Fraction, ByVal v2 As Double) As Double
    175. Return v1.decimalValue - v2
    176. End Operator
    177. Public Shared Operator -(ByVal v1 As Double, ByVal v2 As Fraction) As Double
    178. Return v1 - v2.decimalValue
    179. End Operator
    180. Public Shared Operator *(ByVal v1 As Fraction, ByVal v2 As Double) As Double
    181. Return v1.decimalValue * v2
    182. End Operator
    183. Public Shared Operator *(ByVal v1 As Double, ByVal v2 As Fraction) As Double
    184. Return v1 * v2.decimalValue
    185. End Operator
    186. Public Shared Operator /(ByVal v1 As Fraction, ByVal v2 As Double) As Double
    187. Return v1.decimalValue / v2
    188. End Operator
    189. Public Shared Operator /(ByVal v1 As Double, ByVal v2 As Fraction) As Double
    190. Return v1 / v2.decimalValue
    191. End Operator
    192. Public Shared Operator =(ByVal v1 As Fraction, ByVal v2 As Fraction) As Boolean
    193. Return v1.numerator = v2.numerator AndAlso v1.denominator = v2.denominator
    194. End Operator
    195. Public Shared Operator <>(ByVal v1 As Fraction, ByVal v2 As Fraction) As Boolean
    196. Return Not v1 = v2
    197. End Operator
    198. Public Shared Operator <(ByVal v1 As Fraction, ByVal v2 As Fraction) As Boolean
    199. Return v1.decimalValue < v2.decimalValue
    200. End Operator
    201. Public Shared Operator <(ByVal v1 As Fraction, ByVal v2 As Double) As Boolean
    202. Return v1.decimalValue < v2
    203. End Operator
    204. Public Shared Operator <(ByVal v1 As Double, ByVal v2 As Fraction) As Boolean
    205. Return v1 < v2.decimalValue
    206. End Operator
    207. Public Shared Operator >(ByVal v1 As Fraction, ByVal v2 As Fraction) As Boolean
    208. Return v1.decimalValue > v2.decimalValue
    209. End Operator
    210. Public Shared Operator >(ByVal v1 As Fraction, ByVal v2 As Double) As Boolean
    211. Return v1.decimalValue > v2
    212. End Operator
    213. Public Shared Operator >(ByVal v1 As Double, ByVal v2 As Fraction) As Boolean
    214. Return v1 > v2.decimalValue
    215. End Operator
    216. Public Shared Operator <=(ByVal v1 As Fraction, ByVal v2 As Fraction) As Boolean
    217. Return v1.decimalValue <= v2.decimalValue
    218. End Operator
    219. Public Shared Operator <=(ByVal v1 As Fraction, ByVal v2 As Double) As Boolean
    220. Return v1.decimalValue <= v2
    221. End Operator
    222. Public Shared Operator <=(ByVal v1 As Double, ByVal v2 As Fraction) As Boolean
    223. Return v1 >= v2.decimalValue
    224. End Operator
    225. Public Shared Operator >=(ByVal v1 As Fraction, ByVal v2 As Fraction) As Boolean
    226. Return v1.decimalValue >= v2.decimalValue
    227. End Operator
    228. Public Shared Operator >=(ByVal v1 As Fraction, ByVal v2 As Double) As Boolean
    229. Return v1.decimalValue >= v2
    230. End Operator
    231. Public Shared Operator >=(ByVal v1 As Double, ByVal v2 As Fraction) As Boolean
    232. Return v1 >= v2.decimalValue
    233. End Operator
    234. End Structure
    235. End Namespace

    RodFromGermany schrieb:

    x und 1 sind in jedem Falle eine Triviallösung.
    Was meinst du damit ?
    Es kann sein dass ich mich etwas undeutlich ausgedrückt habe. Unbekannt ist die Zahl nicht direkt, aber ich kann vorher nicht ahnen welche Zahl es wird. (Ähnlich wie Zufallsprinzip)
    Undzwar geht es darum dass ausgehend von einem String jeweils 3 Koordinaten gebildet werden. Anhand dieser 3 berechne ich eine Funktionsgleichung.
    Jenachdem welche Koordinaten dabei sind wird ein Koeffizient (a,b,c) periodisch. Ich muss anschließend allerdings noch mit ihnen rechnen und in periodischer Form ist das nicht so toll.
    Außerdem möchte ich das Gauschverfahren (Aufstellen der Gleichung) direkt mit Brüchen machen, damit ich nicht nachher bei den Decimalzahlen einen Überlauf bekomme.

    Als Beispiel kann man jede beliebige Zahl nehmen.

    LaMiy schrieb:

    Was meinst du damit ?
    Deine Zahl sei x, dann ist x / 1 eine "Triviallösung".
    Gugst Du "Größter gemeinsamer Teiler" und "Kleinstes gemeinsames Vielfaches" als kleine Lektüre zur Zahlentheorie.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Eine periodische Darstellung ist mit normalen Datentypen nicht möglich, denn eine periodische Zahl würde unendlich Speicher benötigen. Du könntest zwar eine spezielle Klasse dafür schreiben, jedoch musst du dann alle Rechenoperationen selbst implementieren, wobei ich nicht mal wüsste, wie man sowas angehen könnte. Auch meine Bruch-Klasse unterstützt keine Periodischen Zahlen, denn der Bruch wird aus einer Dezimalzahl gebildet und diese kann ja nicht periodisch sein.
    Das ist mir schon klar, aber 0,3333333333 ist eben nicht 1/3, sondern ein bisschen weniger. Ein Double kann maximal 15 Stellen darstellen, dann ist Schluss, und auch mit 15 Dreiern hast du 1/3 noch nicht erreicht.
    Ich denke du solltest die Sache anders angehen. Eine periodische Zahl entsteht ja durch eine Division. Statt diese auszurechnen kannst du sie auch gleich als Bruch sehen und dann damit weiterrechnen.
    Hi,

    es bestände die Möglichkeit den Dezimalbruch zu runden. Damit erzielst du zwar ebenfalls ne Abweichung, aber anders geht es ja nicht, denn VB bietet zwar die Möglichkeit mit Gleitkommazahlen zu arbeiten, allerdings können diese bei Zahlen, die sich nur geringfügig von 0 unterscheiden bereits den Verlust von gültigen Stellen im Dezimalbruch auslösen.
    Insbesondere iterativ arbeitende Algorithmen können auf die daraus resultierende Fehlerakkumulation im Einzelfall sehr negativ reagieren und liefern dann normalerweise sogar extrem ungenaue Resultate.
    So musst du dann deine eigene Klasse aufbauen und eben die Datentypen durchprobieren. Was halt dann am Besten passt.
    Ich schlage Long oder ggf sogar Decimal vor. Den Rest musst du halt dann alleine lösen, aber du hast ja bereits mehrere Anfangshilfen bekommen.

    Gruß Trade
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    @LaMiy:: Das war das Beispiel.
    Iwo in meinem Hinterkopf steckt noch drin, dass gewisse periodische Brüche gewissen (mathematischen) Gesetzmäßigkeiten unterliegen, z.B. eine 6-stellige Perriode:
    1 / 7 = 0,142857'142857'142857'142857'14285714
    Genaueres weiß ich dazu nicht.
    -----
    Interessant ist es auch, solch Brüche in einem anderen Zahlensystem zu betrachten, z.B. im Duo-Dezimalsystem. Da die Basis 12 durch 3 teilbar ist, wäre { (1 / 3) = 0,4 | Basis = 12 }.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Das wird anscheinend so eine Art Verschlüsselung, bei der die Zahlen zu Strings konvertiert werden sollen. Statt sie dann in Strings zu konvertieren wäre es sinnvoll, direkt die Rohdaten in Byteform abzuspeichern, also 8 Bytes für Rational64 = UInteger/Integer. Macht sicherheitstechnisch kaum einen Unterschied, ob man das jetzt aus Bytes zurückrechnet oder aus einem String. Performancetechnisch sind natürlich für Bytes wesentlich weniger Operationen nötig.

    Gruß
    ~blaze~