db48x 3 days ago

That’s just a simple linear congruential generator <https://en.wikipedia.org/wiki/Linear_congruential_generator>. In principle it’s possible to devise one that prints anything you want.

  • styczen 3 days ago

    ok, I want word 'Hacker' please find me parameter ;-)

    word 'Linux' will use real number not integer but I'm not remember what to do this. some variable must be 'floor' cut / rouded. I canot find any real solution

  • bjourne 2 days ago

    What if the word contains repeated letters?

    • Jtsummers 2 days ago

      Then it depends on where the repeated letters are, and still may not work. It might work if the repeat letters are the last letters, as in "all", or if it's only a sequence of repeat letters, as in "eeeeeeeee" which has a trivial solution (x0='e', a = 1, c = 0, m > 'e'). The reason is that once you start repeating you've hit a fixed point and you'll never escape it. So words like "hello" and "llama" cannot be generated with this method.

      LCGs also won't create all arbitrary sequences (even if you dismiss the kind mentioned above and just want to generate the rest). I used Z3 to try and find parameters for "Linux" and it failed, while reliably producing an equivalent result for "Ruby" (a, c, and x0 were the same as the original modulo m, but not the exact same numbers) and solutions for other words. I could get as far as "Lin" but "Linu" seemed to be the breaking point.

      Note that a solution might be possible if you change how you interpret the variable `x`. The original interprets `x` directly as a character value for printing which is a pretty severe restriction on the generated sequence. If you only require it to be correct modulo 128 or 256 (for 7- or 8-bit ASCII) then a solution might show up. In fact, that modulo (used for displaying, call it `d`) could also be a parameter to the formula for Z3 to try and find.

      Here's "Ruby" from a Z3 run using this idea:

        f = lambda x: (7 * x + 9995) % 14118
        x0 = -23521
        d = 282
      
      The printing function is:

        def print_series(f, x0, d, length):
          x = x0
          for _ in range(length):
            x = f(x)
            print(chr(x % d), end='')
          print()
      
      The "Linux" search was taking longer than I cared to let it run on my laptop, I don't know if there's a solution using this technique for it.

      Here's my z3 (Python) code, someone better versed in z3 ought to be able to improve this:

        def find_word(word):
          l_max = max(ord(l) for l in word)
          s = z3.Solver()
          f = z3.Function('f', z3.IntSort(), z3.IntSort())
          x0, a, b, m, d = z3.Ints('x0 a b m d')
          s.add(m > l_max)
          s.add(z3.And(m >= d, d > l_max))
          s.add(z3.And(m > a, a >= 0))
          s.add(z3.And(m > b, b >= 0))
          s.add(z3.And(m > x0, x0 > 0))
          x = x0
          for l in word:
              s.add(f(x) == (a * x + b) % m)
              s.add(f(x) % d == ord(l))
              x = f(x)
      
          if s.check() == z3.unsat:
              print('no solution: ' + l)
          else:
              model = s.model()
              print(f'f(x) = ({model[a]} * x + {model[b]}) % {model[m]}')
              print(f'x0 = {model[x0]}, d = {model[d]}')
      
      The lower bounds for `m` and `d` are necessary if you're going to generate every letter in the sequence so adding it at the start seemed reasonable. I restricted (not originally) a, b, and x0 to be within the bounds of [0,m) and (0,m) for x0 (0 is always fixed-point and won't be what we want to print for this exercise so it can be skipped). This doesn't seem to have a clear performance impact though I expected it to, it does make the printout clearer since everything is in a "reduced" form.
      • bjourne a day ago

        Cool!

        > If you only require it to be correct modulo 128 or 256 (for 7- or 8-bit ASCII) then a solution might show up.

        Another variant I thought of is to not require the character to be picked from the least bits of x. Still seem very difficult to find the right parameters for generating arbitrary strings though.

  • swiatlo 3 days ago

    m = 1062 a = 23 c = 984 x0 = 608