3
\$\begingroup\$

About exercise 2.2


Place N queens on N*N board, 1 queen per line, so that all queens are safe.

Use permutation to improve the provided implementation from book.


The exercise 2.2 asked to do it via permutation, and I did that with recursion, using shallow copy before passing arrays to next level of recursion.

Things I think might need review:

  1. I'm very new to lua; is there some lua specific improvements can be done to my code (I googled while programming this exercise).
  2. How to improve the algorithm? e.g., avoid the shallow copy?
  3. Parallel programming to speed up?

Code


eight_queen_perm.lua:

--local inspect = require('inspect')

N = 8   -- board size
z = 0   -- follow performance, by counting the time isplaceok() is called,


-- check whether position (n,c) is free from attacks
function isplaceok (a, n, c)
    z = z + 1
    for i = 1, n - 1 do
        -- for each queen already placed
        if (a[i] == c) or -- same column?
                (a[i] - i == c - n) or -- same diagonal?
                (a[i] + i == c + n) then
            -- same diagonal?
            return false            -- place can be attacked
        end
    end
    return true    -- no attacks; place is OK
end

-- print a board
function printsolution (a)
    for i = 1, N do
        -- for each row
        for j = 1, N do
            -- and for each column
            -- write "X" or "-" plus a space
            io.write(a[i] == j and "Q" or "-", " ")
        end
        io.write("\n")
    end
    io.write("\n")
end

-- add queen to board, via checking permutation,
function queenPermRec(seq, prefix, arr)
    if #arr == 0 then
        -- prefix is the result,
        seq = seq + 1
        --io.write(string.format("[%d] %s\n", seq, inspect(prefix)))
        io.write(string.format("(%d)\n", seq))
        printsolution(prefix)
        return seq
    end

    for i = 1, #arr do
        if not isplaceok(prefix, #prefix + 1, arr[i]) then
            -- stop early if not valid,
            goto skip_to_next
        end

        prefix2 = table.shallow_copy(prefix)
        table.insert(prefix2, arr[i])

        arr2 = table.shallow_copy(arr)
        table.remove(arr2, i)
        seq = queenPermRec(seq, prefix2, arr2)

        :: skip_to_next ::
    end
    return seq
end

function table.shallow_copy(t)
    local t2 = {}
    for k, v in pairs(t) do
        t2[k] = v
    end
    return t2
end

function table.join(t1, t2)
    for i = 1, #t2 do
        t1[#t1 + 1] = t2[i]
    end
    return t1
end

-- generate increase array in [1, n],
function genIncArr(n)
    arr = {}
    for i = 1, n do
        arr[i] = i
    end
    return arr
end

queenPermRec(0, {}, genIncArr(N))
io.write(string.format("isplaceok() called %d times\n", z))
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Regarding avoid shallow copy, I wrote another version that achieve it via:

  • Swap before recursion.
  • Do recursion
  • Swap back after recursion.

Thus there is only 1 copy of array during the execution.


Code

eight_queen_perm_inplace.lua:

--local inspect = require('inspect')
-- refer:   https://codereview.stackexchange.com/questions/293717       # SO question - code review,
N = 8   -- board size
z = 0   -- follow performance, by counting the time isplaceok() is called,


-- check whether position (n,c) is free from attacks
function isplaceok (a, n, c)
    z = z + 1
    for i = 1, n - 1 do
        -- for each queen already placed
        if (a[i] == c) or -- same column?
                (a[i] - i == c - n) or -- same diagonal?
                (a[i] + i == c + n) then
            -- same diagonal?
            return false            -- place can be attacked
        end
    end
    return true    -- no attacks; place is OK
end

-- print a board
function printsolution (a)
    for i = 1, N do
        -- for each row
        for j = 1, N do
            -- and for each column
            -- write "X" or "-" plus a space
            io.write(a[i] == j and "Q" or "-", " ")
        end
        io.write("\n")
    end
    io.write("\n")
end

-- add queen to board, via checking permutation,
function queenPermRecInplace(seq, arr, idx)
    if idx == #arr then
        -- need check last level,
        if isplaceok(arr, idx, arr[idx]) then
            seq = seq + 1
            --io.write(string.format("[%d] %s\n", seq, inspect(arr)))
            io.write(string.format("(%d)\n", seq))
            printsolution(arr)
        end
        return seq
    end

    for i = idx, #arr do
        -- swap, before recursion,
        swapArrElements(arr, idx, i)

        if isplaceok(arr, idx, arr[idx]) then
            -- stop early if not valid,
            seq = queenPermRecInplace(seq, arr, idx + 1)
        end

        -- swap back, after recursion,
        swapArrElements(arr, idx, i)
    end
    return seq
end

function swapArrElements(arr, a, b)
    tmpValue = arr[a]
    arr[a] = arr[b]
    arr[b] = tmpValue
end

function table.shallow_copy(t)
    local t2 = {}
    for k, v in pairs(t) do
        t2[k] = v
    end
    return t2
end

function table.join(t1, t2)
    for i = 1, #t2 do
        t1[#t1 + 1] = t2[i]
    end
    return t1
end

-- generate increase array in [1, n],
function genIncArr(n)
    arr = {}
    for i = 1, n do
        arr[i] = i
    end
    return arr
end

queenPermRecInplace(0, genIncArr(N), 1)
io.write(string.format("isplaceok() called %d times\n", z))

Concerns

  • This is fine for single thread execution, to achieve parallel, copy array is necessary I think.
  • Though there is no array copy, but it's not necessarily faster, in fact it might be slower than the array copy version.
  • The permutation is not exactly in order now.
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.