Search code examples
randomprologclpfd

Generate n-digit number w/o 1s and 0s in prolog


I am a beginner to prolog. The task is to generate n-digit numbers without using ones and zeroes. How would this be done? Do you generate random numbers and then delete 1s and 0s (sounds inefficient)?


Solution

  • What you describe is definitely one way to do it. As you mention, it is not a particularly good way to do it, since it is so inefficient.

    Nevertheless, the approach you mention is so common that it has its own name: It is often referred to as generate and test, since we first generate, and then either reject or accept the solution, or modify it further so that it satisfies all constraints.

    A typically much more efficient approach is to first constrain the whole solution so that all requirements are expressed, and then to let the system search only within the already constrained space. This is especially easy to do in Prolog since it provides built-in constraints that you can post before you even start the search for solutions, and they will be automatically taken into account before and also during the search.

    For example, you could do it as follows, using your Prolog system's CLP(FD) constraints to express the desired requirements over integers:

    n_digits(N, Ds, Num) :-
            length(Ds, N),
            Ds ins 2..9,
            reverse(Ds, Rs),
            foldl(pow10, Rs, 0-0, _-Num).
    
    pow10(D, Pow0-S0, Pow-S) :-
            Pow #= Pow0 + 1,
            S #= D*10^Pow0 + S0.
    

    Thus, we represent the number as a list of digits, and relate this list to the corresponding integer using suitable constraints. The key point is that all this happens before a single solution is actually generated, and all solutions that are found will satisfy the stated constraints. Moreover, this is a very general relation that works in all directions, and we can use it to test, generate and complete solutions.

    Here is an example query, asking for how such numbers with 3 digits look like in general:

    ?- n_digits(3, Ds, N).
    

    In response, we get:

    Ds = [_11114, _11120, _11126],
    _11114 in 2..9,
    _11114*100#=_11182,
    _11182 in 200..900,
    _11182+_11236#=N,
    _11236 in 22..99,
    _11282+_11126#=_11236,
    _11282 in 20..90,
    _11120*10#=_11282,
    _11120 in 2..9,
    _11126 in 2..9,
    N in 222..999.
    

    We can use label/1 to obtain concrete solutions:

    ?- n_digits(3, Ds, N), label(Ds).
    Ds = [2, 2, 2],
    N = 222 ;
    Ds = [2, 2, 3],
    N = 223 ;
    Ds = [2, 2, 4],
    N = 224 ;
    Ds = [2, 2, 5],
    N = 225 ;
    etc.
    

    When describing tasks where integers are involved, CLP(FD) constraints are often a very good fit and allow very general solutions.

    For example, it is easy to incorporate additional requirements:

    ?- n_digits(3, Ds, N),
       N #> 300,
       label(Ds).
    Ds = [3, 2, 2],
    N = 322 ;
    Ds = [3, 2, 3],
    N = 323 ;
    Ds = [3, 2, 4],
    N = 324 ;
    etc.
    

    We're not in Sparta anymore. See for more information!