3

I have an Array of chars, which is completely random except that every character occurs at most once.

I also have a string containing only characters that are present in the array. I want this string to be "counted upwards" (if that makes sense), like "111" becomes "112", or "aaa" becomes "aab".

Let´s say the Array of chars contains 1, 2, and 3. The example above works, but when the string is "333" (should become "1111"), my function returns an empty - or wrong - string.

If I call the function again, providing the wrong string, after a few times it returns the correct value ("1111").

Why does this happen?

This is my function:

Public Function getNextString(ByVal currentStr As String, ByVal pattern() As Char) As String 
    'currentStr is the string which I want to count upwards, pattern() is the array of chars

    Dim nextStr As String = ""

    Dim currentStrArray() As Char = currentStr.ToCharArray
    Dim currenStrPosition As Integer = currentStrArray.Length - 1

    Dim finished As Boolean = False
    Do Until finished = True
        Dim newPosition As Integer = getPositionInArray(currentStrArray(currentStrPosition)) 'this is a custom function, should be self-explaining
        If newPosition = Nothing Then Return Nothing
        newPosition += 1

        Try
            currentStrArray(currenStrPosition) = pattern(newPosition)
            finished = True
        Catch ex As IndexOutOfRangeException
            currentStrArray(currentStrPosition) = pattern(0)
            currentStrPosition -= 1
        End Try

        If currentStrPosition < 0 Then
            nextStr = pattern(0)
            finished = True
        End If
    Loop

    For i As Integer = 0 To currentStrArray.Length - 1
        nextStr = nextStr & currentStrArray(i)
    Next

    Return nextStr
End Function

Any ideas?

EDIT:

As an example, I have the array {"1","2","3"}. My string is first "111". I want to test these strings hashsums. After testing that string, I need the next string, "112". Then, "113", "121", "122", and so on. When the string reaches "333" and the string is not the one I'm looking for, it was obviously no string with only 3 characters (all 3-character-combinations possible with the array have been tried). So I need to start again using 4 characters. That´s why "333" is supposed to become "1111". Hope this helps.

EDIT #2:

I found the error. I redimensioned my array incorrectly, so the last index was empty. This made my strings look weird. Thank you all for your working solutions, have a good day!

Ian
  • 30,182
  • 19
  • 69
  • 107
Bumblebee
  • 35
  • 5
  • 2
    Why should 333 become 1111? – Tim Schmelter Jan 27 '16 at 12:21
  • I want to count upwards using only the characters in the array. if it contains only 1, 2, and 3, it should only use these. Like if you have all 10 digits, 99 becomes 100. – Bumblebee Jan 27 '16 at 12:26
  • Still not clear. You should show your sample array and string. Even if it is `{"1","2","3"}` i dont understand why `333` becomes `1111`. Why is 333+1=1111? – Tim Schmelter Jan 27 '16 at 12:30
  • 1
    I think that it's basically substituting the numbers with random chars, so if the array is `{"a", "b", "c"}` you should treat "a" as 0, "b" as 1, "c" as 3, and count in a base 3. – Zohar Peled Jan 27 '16 at 12:35
  • @Zohar Peled These characters can be any Ascii characters, and the array size can depend from only 1 char to 256... how would i do that – Bumblebee Jan 27 '16 at 12:44
  • @Ian Still don´t get why that requirement is that funny... it seems to be quite logical to me! I like your answer though, I´d upvote it if I had the required reputation. – Bumblebee Jan 27 '16 at 14:43
  • @Ian no offense taken :) It´s just like it´s not calculation, but more like brute forcing. Trying all combinations possible, which has nothing to do with maths in my eyes! btw, is there anything I need to accept or something after my question was edited? – Bumblebee Jan 27 '16 at 14:52
  • I see! thanks for introducing me to the basics of this site :) – Bumblebee Jan 27 '16 at 15:00
  • @TimSchmelter - I think you're spot on with `"333" + 1 <> "1111"`. Take a look at my answer - I've shown that `"333" + 1 = "2111"`. – Enigmativity Jan 27 '16 at 15:16

3 Answers3

0

If I understand correctly you want to increment in a specific base (length of your array). What you need is a mapping function that maps your array chars to their respective numbers, then convert from your base to decimal, increment, convert back and remap.

Can you check if this link helps? Quickest way to convert a base 10 number to any base in .NET?

I just wrote the following in c#, it seems to work.

    static void Main(string[] args)
    {
        char[] definition = new char[] { 'a', 'b', 'c', 'd', '9', 'x', 'y', 'z', '1', '2', '3'};
        string helperstring = new String(definition);
        int basenumber = definition.Length;
        string mynumberasstring = "333";
        Console.WriteLine(mynumberasstring);
        int correspondingdecimal = 0;
        for (int i = 0; i < mynumberasstring.Length; i++)
        {
            char x = mynumberasstring[mynumberasstring.Length - i - 1];
            int index = helperstring.IndexOf(x);
            int magnitude = 1;
            for (int j = 0; j < i; j++)
                magnitude *= basenumber;
            Console.WriteLine(x + " -> " + index);
            correspondingdecimal += magnitude * index;
        }
        Console.WriteLine(correspondingdecimal + " -> " + ++correspondingdecimal);

        List<int> indicesofnewnumber = new List<int>();

        int newmagnitude = basenumber;
        while(correspondingdecimal > 0)
        {
            int div = correspondingdecimal / basenumber;
            int remainder = correspondingdecimal % basenumber;

            Console.WriteLine("{0} -> {1} ; {2}", correspondingdecimal, div, remainder);

            indicesofnewnumber.Add(remainder);
            correspondingdecimal = div;
        }
        string newnumberasstring = "";
        for (int i = 0; i < indicesofnewnumber.Count; i++)
            newnumberasstring += definition[indicesofnewnumber[indicesofnewnumber.Count - 1 - i]];

        Console.WriteLine(newnumberasstring);
        Console.ReadLine();
    }
Community
  • 1
  • 1
Geoffrey
  • 956
  • 1
  • 10
  • 16
  • Using my function it counts upwards using the array´s index (is that the correct term?). Like, "a" in {"a","b"} is index 0; I replace it with index 1. Maybe I´m misunderstanding you...? Not sure – Bumblebee Jan 27 '16 at 12:52
  • I would map a -> 0; b -> 1. So "ba" would become "10", which can be converted to 2; incremented 3 becomes "11" which is mapped to "bb". – Geoffrey Jan 27 '16 at 12:55
0

Well, I would convert to a number, add 1 and then convert back to a string.

Convert the string to a number:

Function ConvertStringToNumber(input As String, pattern As Char()) As Integer
    Dim number As Integer = 0
    Dim charDigits = pattern.ToList()
    Dim numberBase = charDigits.Count
    For i As var = 0 To input.Length - 1
        Dim digit = charDigits.IndexOf(input(input.Length - 1 - i))
        If digit <> -1 Then
            number += digit * CInt(Math.Pow(numberBase, i))
        End If
    Next
    Return number
End Function

Convert the number back to a string:

Function convertNumberToString(number As Integer, pattern As Char()) As String
    Dim charDigits = pattern.ToList()
    Dim numberBase = charDigits.Count
    Dim buffer = New Stack(Of Char)()
    'var j = buffer.Length;
    While number > 0
        buffer.Push(charDigits(number Mod numberBase))
        number = number / numberBase
    End While
    Dim sb = New StringBuilder()
    While buffer.Count > 0
        sb.Append(buffer.Pop())
    End While

    Return sb.ToString()
End Function

Now that you have conversion functions, all you have to do is something like this:

Function getNextString(ByVal currentStr As String, ByVal pattern() As Char) As String
    Dim number = ConvertStringToNumber(currentStr, pattern)
    number += 1
    Return convertNumberToString(number, pattern)
End Function
Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
  • btw, "333" should be converted to "2111" in the example you wrote in your question, since "1111" is basically 0000. – Zohar Peled Jan 27 '16 at 13:41
  • Accepted as it´s easy and working and also in vb. See edit #2 in the question – Bumblebee Jan 27 '16 at 14:23
  • I wrote my answer in c# and converted to vb.net using an online tool. I suspect that the conversion was less then perfect since [the c# version works as I intended.](https://dotnetfiddle.net/7MJ1MV) – Zohar Peled Jan 27 '16 at 14:42
  • You´re right. Of course it is working. but when I tested with my application, it didn´t, as I made a mistake elsewhere. I removed my comment. – Bumblebee Jan 27 '16 at 14:58
  • BTW, this code doesn't compile with `Option Strict On`. – Enigmativity Jan 27 '16 at 15:21
  • @Enigmativity Well, it's been a long time since I last wrote vb.net. Moved to c# and didn't look back a few years ago, so if I've made some mistakes in my answer I hope that the StackOverflow community can forgive me for that. – Zohar Peled Jan 27 '16 at 15:29
  • @ZoharPeled - LOL, I hope you didn't take offense! You would only need to change `number = number / numberBase` to `number = number \ numberBase` to avoid an invalid cast from `Double` to `Integer`. – Enigmativity Jan 27 '16 at 15:36
  • @Enigmativity none taken :-) – Zohar Peled Jan 27 '16 at 15:37
0

Here's the smallest code that I can think of to convert a number into an arbitrary base with arbitrary digits:

Dim convert As Func(Of Integer, Char(), String) = Nothing
convert = Function (n, cs) _
    If(n \ cs.Length = 0, "", convert(n \ cs.Length, cs)) + cs(n Mod cs.Length)

So, to convert 4 to binary I could do convert(4, { "0"c, "1"c }) and I would get 100.

As a sanity check if I view the first 32 (starting from zero) binary numbers using String.Join(", ", Enumerable.Range(0, 32).Select(Function (n) convert(n, { "0"c, "1"c }))) and I get this:

0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111, 10000, 10001, 10010, 10011, 10100, 10101, 10110, 10111, 11000, 11001, 11010, 11011, 11100, 11101, 11110, 11111

Or the first 102 decimal numbers starting from zero with String.Join(", ", Enumerable.Range(0, 102).Select(Function (n) convert(n, "0123456789".ToCharArray()))) I get this:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101

So I can now use the same kind of code to get the first 50 numbers from the digits { "1"c, "2"c, "3"c } using String.Join(", ", Enumerable.Range(0, 50).Select(Function (n) convert(n, "123".ToCharArray()))) and I get this:

1, 2, 3, 21, 22, 23, 31, 32, 33, 211, 212, 213, 221, 222, 223, 231, 232, 233, 311, 312, 313, 321, 322, 323, 331, 332, 333, 2111, 2112, 2113, 2121, 2122, 2123, 2131, 2132, 2133, 2211, 2212, 2213, 2221, 2222, 2223, 2231, 2232, 2233, 2311, 2312, 2313, 2321, 2322

This is the correct "counting" using the digits { "1"c, "2"c, "3"c }. So, it would be correct to say that "333" + 1 = "2111" in this numbering system. This is the same as saying 999 + 1 = 1000 in our decimal system - it clearly doesn't go from 999 + 1 = 0000 which is the implication that "333" + 1 = "1111" would mean in this "123" system. Strictly speaking I should have written "333" + "2" = "2111" (since "2" represents 1).

If you want all combinations then you would need to pad the strings to the left with the "zero" character - in this case 1. Then this code:

String.Join(", ", _
    Enumerable _
        .Range(0, 500) _
        .Select(Function (n) convert(n, "123".ToCharArray()).PadLeft(4, "1"c)) _
        .Where(Function (x) x.Length = 4))

...gives the following 81 numbers:

1111, 1112, 1113, 1121, 1122, 1123, 1131, 1132, 1133, 1211, 1212, 1213, 1221, 1222, 1223, 1231, 1232, 1233, 1311, 1312, 1313, 1321, 1322, 1323, 1331, 1332, 1333, 2111, 2112, 2113, 2121, 2122, 2123, 2131, 2132, 2133, 2211, 2212, 2213, 2221, 2222, 2223, 2231, 2232, 2233, 2311, 2312, 2313, 2321, 2322, 2323, 2331, 2332, 2333, 3111, 3112, 3113, 3121, 3122, 3123, 3131, 3132, 3133, 3211, 3212, 3213, 3221, 3222, 3223, 3231, 3232, 3233, 3311, 3312, 3313, 3321, 3322, 3323, 3331, 3332, 3333

...and 81 is the total number of combinations as 3 x 3 x 3 x 3 = 81.


Here are the pair of functions to convert both ways:

Function Convert(number As Integer, digits As Char()) As String
    Dim r = digits(number Mod digits.Length).ToString()
    If number \ digits.Length <> 0 Then
        r = Convert(number \ digits.Length, digits) + r
    End If
    Return r
End Function

Function Convert(number As String, digits As Char()) As Integer
    Dim r = Array.IndexOf(digits, number(0))
    If number.Substring(1).Length > 0 Then
        r = r * digits.Length + Convert(number.Substring(1), digits)
    End If
    Return r
End Function
Enigmativity
  • 113,464
  • 11
  • 89
  • 172