I am trying to write an algorithm that will calculate the Nth root of a number that is provided to the algorithm (practically) as a string, in C#. What I have right now calculates the square root, but not other roots, and I want to modify the part of the algorithm that is square root specific in order to generalize it to Nth roots.
I have been using two WikiHow articles (URLs in code comments, below) on calculating square and Nth roots manually, and I believe that the only part of what I have that is specific to the square root approach is isolated in my local function, (long, long) GetDigit( long segment )
. I don't understand the math in the Nth root approach that differs from the square root approach, and I am doing this for programming practice and to develop my own number library that will then be generally useful, not as a math exercise. I am hoping that someone will see this question that already knows C# and the math involved.
I say that the number is provided to the algorithm "practically" as a string because I am developing my own "Number" type that stores both a numerator and a denominator as BigIntegers, so that it has the unlimited size benefit of BigIntegers and also supports decimal points.
For the purposes of this question, the algorithm might as well be receiving the number as a string, so I have modified it slightly so that it will accept and use a string if the Number-type parameter (changed to an object parameter in the code below) is null. That way you should easily be able to test any modifications you make without having to worry about the details of my Number type.
public static string NthRoot( int nth, object aNumber, string aStringInteger = "" )
{
Debug.WriteLine( $"\nFINDING THE {nth}TH ROOT OF {aNumber}." );
string[] Segments = GetSegments( aNumber?.Integer.ToString( ) ?? aStringInteger );
Debug.WriteLine( $"Segment Count: {Segments.Length}." );
string Result = "";
long SegmentIndex = 0;
long NextSegment = Convert.ToInt64( Segments[SegmentIndex] );
long CurrentDigit = 0;
long SubtractAmount;
(CurrentDigit, SubtractAmount) = GetDigit( NextSegment );
Result += CurrentDigit.ToString( );
Debug.WriteLine( $"{Result}." );
bool DoForLoop( )
{
for( ; SegmentIndex < Segments.Length; SegmentIndex++ )
{
NextSegment = (NextSegment - SubtractAmount) * (int)Math.Pow( 10, nth );
NextSegment += Convert.ToInt64( Segments[SegmentIndex] );
(CurrentDigit, SubtractAmount) = GetDigit( NextSegment );
Debug.WriteLine( $"Adding {CurrentDigit} to result string." );
Result += CurrentDigit.ToString( );
Debug.WriteLine( $"{Result}." );
if( Math.Pow( Result, nth ) == aNumber ) return true;
}
return false;
}
SegmentIndex++;
if( DoForLoop( ) ) return Result;
Result += ".";
Segments = GetSegments( aNumber?.Decimal( ).Replace( "~", "" ) ?? "", false, 20 );
//My Number type appends a ~ to repeating decimals after two repetitions of the repeating pattern.
SegmentIndex = 0;
DoForLoop( );
return Result;
string[] GetSegments( string remaining, bool padOnLeft = true, int extraPaddingOnRight = 0 )
{
int SegmentCount = remaining.Length / nth; //This is the number of complete nth-long sequences.
if( remaining.Length % nth != 0 ) SegmentCount++;
int PadLeft;
if( padOnLeft )
{
PadLeft = (SegmentCount * nth) - remaining.Length;
Debug.WriteLine( $"Padding the left with {PadLeft} zeros." );
}
else
{
PadLeft = 0;
remaining = remaining.PadRight( SegmentCount * nth + extraPaddingOnRight, '0' );
SegmentCount = remaining.Length / nth;
}
string[] _Segments = new string[SegmentCount];
for( long s = 0; s < SegmentCount; s++ )
{
_Segments[s] = remaining.Substring( 0, nth - PadLeft );
remaining = remaining.Substring( nth - PadLeft );
PadLeft = 0;
}
return _Segments;
}
(long, long) GetDigit( long segment )
{
if( segment == 0 ) return (0, 0);
long Candidate = 0;
long Subtract = 0;
long NextCandidate = Candidate + 1;
if( nth != 2 ) throw new NotImplementedException(
"The lines that follows this exception are where I do not know how to generalize the square root " +
"approach to an Nth root approach." );
//What I have is based on: method 2 at https://m.wikihow.com/Calculate-a-Square-Root-by-Hand
//This appears to be the only part that differs from: https://www.wikihow.com/Find-Nth-Roots-by-Hand
//The math at issue is in step 4 for Nth roots and steps 4 to 6 for square roots.
string LeftNext = (CurrentResult( ) * 2).ToString( ) + NextCandidate.ToString( );
long SubtractNext = Convert.ToInt64( LeftNext ) * NextCandidate;
Debug.WriteLine( $"GetDigit( {segment} ):" );
while( SubtractNext <= segment )
{
Candidate = NextCandidate;
Subtract = SubtractNext;
NextCandidate++;
LeftNext = (CurrentResult( ) * 2).ToString( ) + NextCandidate.ToString( );
SubtractNext = Convert.ToInt64( LeftNext ) * NextCandidate;
}
return (Candidate, Subtract);
}
long CurrentResult( ) => Result == "" ? 0 : Convert.ToInt64( Result.Replace( ".", "" ) );
}