简体   繁体   中英

How to increment a large number as a string as if it were an integer?

I've been trying to think of ways to do it, I can't just convert the string to an int. It's too long to be an int, that's the reason I need to do this. I can do it for single digits, but as soon as I want to add say 10 or 20 I'm not sure how to change my code to allow that. If I increase the loop it will increase the length of the string and that wouldn't be treating it as an actual number. The reason I include two languages is because I will accept C# or F# code in an answer.

open System

type String with
    member this.Last() =
        this.[this.Length - 1]

let mutable s = "1234567890" // the numbers I will use are a lot bigger than this.

for i in 0 .. 9 do // 9 is max before it increases the length of the string.
    printfn "%s" s

    let last = (s.Last().ToString() |> int) + 1

    s <- s.[0 .. s.Length - 2] + last.ToString()

Console.Read() |> ignore

I doubt there is any situation where using strings to represent numbers would be a good idea - you can use BigInteger to represent and work with large numbers and it supports all numerical operations, not just incrementing by one.

However, it is a fun problem, so here is one possible F# solution:

let incString (s:string) = 
  let rec loop acc carry i (s:string) =  
    match i, carry with 
    | -1, true -> System.String(Array.ofList ('1'::acc))
    | -1, false -> System.String(Array.ofList acc)
    | _ ->
      match s.[i], carry with 
      | '9', true -> loop ('0'::acc) true (i-1) s 
      | c, false -> loop (c::acc) false (i-1) s
      | c, true -> loop (char ((int c) + 1)::acc) false (i-1) s
  loop [] true (s.Length-1) s

incString "9899"

The inner loop function takes the following parameters:

  • acc is an accumulator, used to collect digits of the new number (starting from the end)
  • carry is a boolean flag that is true if we want to add 1 to the next number (this is initially true and then it is also true as long as the digits are 9 - because 9 will become 0 and we then need to add to the next digit in the string).

  • i is the current index, starting from the last one

  • s is the input string representing the original number.

you reverse it into a list of chars the try to increment the lowest digit, if that doesnt work (its a 9), you set the lowest digit to '0' and recurse the tail

let inc: string -> string =
    let rec incRev: List<char> -> List<char> =             
        function
        | [] -> [ '1' ]
        | '9' :: cs -> '0' :: incRev cs
        | c :: cs -> char (int c + 1) :: cs
    fun s -> 
        s.ToCharArray() 
        |> Array.toList 
        |> List.rev
        |> incRev
        |> List.rev
        |> Array.ofList
        |> System.String

another probably slightly more efficient answer (I prefer the previous one), but doesnt have all the reversing going on. you fold from the back of the list of chars assembling an answer and passing the carry digit up to the end.

let inc2: string -> string =
    let incDigit : (char * (List<char> * char)) -> (List<char> * char) =
        function 
        | (c,(a,'0'))   -> (c :: a,'0')
        | ('9',(a,'1')) -> ('0' :: a,'1')
        | (c,(a,'1'))   -> ((char (int c + 1)) :: a,'0')            
    fun s -> 
        let result = 
            (s.ToCharArray() 
            |> Array.toList 
            |> List.foldBack (fun c state -> incDigit (c,state))) ([],'1')
        result
        |> fst
        |> if (snd result = '1') then (fun cs -> '1' :: cs) else id
        |> Array.ofList
        |> System.String

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM