計算機科学のブログ

Building Abstractions with Data - Hierarchical Data and the Closure Property - Sequences as Conventional Interfaces - Nested Mappings - flatmap, order, eight-queens puzzle

Structure and Interpretation of Computer Programs: JavaScript Edition(Harold Abelson(著)、Gerald Jay Sussman(著)、Julie Sussman(著)、The MIT Press)のChapter 2(Building Abstractions with Data)、2.2(Hierarchical Data and the Closure Property)、2.2.3(Sequences as Conventional Interfaces)、Nested Mappings、Exercise 2.43の解答を求めてみる。

queens_col関数の呼び出し回数について考える。

exercise 2.42ではqueens_col(k)の呼び出しの中でqueens(k - 1)を呼び出し、それについて1からboard_sizeを追加。

exercise 2.43ではqueens_col(k)の呼び出しの中でboard_size回queens_col(k - 1)を呼び出す。

よってqueens(board_size)内でのqueens_colsの呼び出し回数はexercise 2.42ではboard_size回、exercise 2.43でのqueens_colの呼び出し回数は1 + board_size^1 + board_size^2 + board_size^(board_size - 1) = (1-board_size^board_size) / (1 - board_size)回。

ゆえに、exercise 2.42の時間をTとすれば、exercise 2.43の時間は(1-board_size^board_size) / ((1-board_size) board_size)

コード

const { performance } = require('perf_hooks');

function stringfy(x) {
    if (x === undefined) {
        return 'undefined';
    }
    if (is_null(x)) {
        return "null";
    }
    if (is_pair(x)) {
        return "[" + stringfy(head(x)) + ", " + stringfy(tail(x)) + "]";
    }
    return x.toString();
}
function pair(x, y) {
    return [x, y];
}
function head(z) {
    return z[0];
}
function tail(z) {
    return z[1];
}
function is_pair(x) {
    return Array.isArray(x);
}
function is_null(x) {
    return x === null;
}
function list(...args) {
    return args.length === 0 ?
        null :
        pair(args[0], list(...args.slice(1)));
}
function display(x) {
    return console.log(stringfy(x));
}
function length(sequence) {
    return is_null(sequence) ?
        0 :
        1 + length(tail(sequence));
}
function accumulate(op, initial, sequence) {
    return is_null(sequence) ?
        initial :
        op(
            head(sequence),
            accumulate(op, initial, tail(sequence))
        );
}
function map(f, seq) {
    return accumulate((x, y) => pair(f(x), y), null, seq);
}
function filter(pred, seq) {
    if (is_null(seq)) {
        return null;
    }
    const x = head(seq);
    return pred(x) ?
        pair(x, filter(pred, tail(seq))) :
        filter(pred, tail(seq));
}
function append(seq1, seq2) {
    return accumulate(pair, seq2, seq1)
}
function enumerate_interval(low, high) {
    return low > high ?
        null :
        pair(low, enumerate_interval(low + 1, high));
}
function flatmap(f, seq) {
    return accumulate(append, null, map(f, seq));
}
const empty_board = null;
function adjoin_position(row, k, queens) {
    return pair(row, queens);
}
function is_safe(k, positions) {
    function iter(position, rest, n) {
        if (is_null(rest)) {
            return true;
        }
        const i = head(rest);
        return position !== i &&
            position - i != n &&
            i - position != n &&
            iter(position, tail(rest), n + 1);
    }
    return iter(head(positions), tail(positions), 1);
}
function queens(board_size) {
    function queen_cols(k) {
        return k === 0 ?
            list(empty_board) :
            filter(
                positions => is_safe(k, positions),
                flatmap(
                    rest_of_queens => map(
                        new_row =>
                            adjoin_position(
                                new_row,
                                k,
                                rest_of_queens
                            ),
                        enumerate_interval(1, board_size)
                    ),
                    queen_cols(k - 1)
                ),
            );
    }
    return queen_cols(board_size);
}
function queens1(board_size) {
    function queen_cols(k) {
        return k === 0 ?
            list(empty_board) :
            filter(
                positions => is_safe(k, positions),
                flatmap(
                    new_row => map(
                        rest_of_queens =>
                            adjoin_position(
                                new_row,
                                k,
                                rest_of_queens
                            ),
                        queen_cols(k - 1)
                    ),
                    enumerate_interval(1, board_size)
                ),
            );
    }
    return queen_cols(board_size);
}
const board_size = 7;
let t = performance.now();
const qs = queens(board_size);

t = performance.now() - t;
display(t);
display(qs);
display(length(qs));

let t1 = performance.now();
const qs1 = queens1(board_size);
t1 = performance.now() - t1;
display(t1);
display(qs1);
display(length(qs1));

入出力結果(Terminal, Zsh)

 % node answer2.43.js
9.040051996707916
[[6, [4, [2, [7, [5, [3, [1, null]]]]]]], [[5, [2, [6, [3, [7, [4, [1, null]]]]]]], [[4, [7, [3, [6, [2, [5, [1, null]]]]]]], [[3, [5, [7, [2, [4, [6, [1, null]]]]]]], [[6, [3, [5, [7, [1, [4, [2, null]]]]]]], [[7, [5, [3, [1, [6, [4, [2, null]]]]]]], [[6, [3, [7, [4, [1, [5, [2, null]]]]]]], [[6, [4, [7, [1, [3, [5, [2, null]]]]]]], [[6, [3, [1, [4, [7, [5, [2, null]]]]]]], [[5, [1, [4, [7, [3, [6, [2, null]]]]]]], [[4, [6, [1, [3, [5, [7, [2, null]]]]]]], [[4, [7, [5, [2, [6, [1, [3, null]]]]]]], [[5, [7, [2, [4, [6, [1, [3, null]]]]]]], [[1, [6, [4, [2, [7, [5, [3, null]]]]]]], [[7, [4, [1, [5, [2, [6, [3, null]]]]]]], [[5, [1, [6, [4, [2, [7, [3, null]]]]]]], [[6, [2, [5, [1, [4, [7, [3, null]]]]]]], [[5, [7, [2, [6, [3, [1, [4, null]]]]]]], [[7, [3, [6, [2, [5, [1, [4, null]]]]]]], [[6, [1, [3, [5, [7, [2, [4, null]]]]]]], [[2, [7, [5, [3, [1, [6, [4, null]]]]]]], [[1, [5, [2, [6, [3, [7, [4, null]]]]]]], [[3, [1, [6, [2, [5, [7, [4, null]]]]]]], [[2, [6, [3, [7, [4, [1, [5, null]]]]]]], [[3, [7, [2, [4, [6, [1, [5, null]]]]]]], [[1, [4, [7, [3, [6, [2, [5, null]]]]]]], [[7, [2, [4, [6, [1, [3, [5, null]]]]]]], [[3, [1, [6, [4, [2, [7, [5, null]]]]]]], [[4, [1, [3, [6, [2, [7, [5, null]]]]]]], [[4, [2, [7, [5, [3, [1, [6, null]]]]]]], [[3, [7, [4, [1, [5, [2, [6, null]]]]]]], [[2, [5, [7, [4, [1, [3, [6, null]]]]]]], [[2, [4, [1, [7, [5, [3, [6, null]]]]]]], [[2, [5, [1, [4, [7, [3, [6, null]]]]]]], [[1, [3, [5, [7, [2, [4, [6, null]]]]]]], [[2, [5, [3, [1, [7, [4, [6, null]]]]]]], [[5, [3, [1, [6, [4, [2, [7, null]]]]]]], [[4, [1, [5, [2, [6, [3, [7, null]]]]]]], [[3, [6, [2, [5, [1, [4, [7, null]]]]]]], [[2, [4, [6, [1, [3, [5, [7, null]]]]]]], null]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
40
504.73032903671265
[[1, [3, [5, [7, [2, [4, [6, null]]]]]]], [[1, [4, [7, [3, [6, [2, [5, null]]]]]]], [[1, [5, [2, [6, [3, [7, [4, null]]]]]]], [[1, [6, [4, [2, [7, [5, [3, null]]]]]]], [[2, [4, [1, [7, [5, [3, [6, null]]]]]]], [[2, [4, [6, [1, [3, [5, [7, null]]]]]]], [[2, [5, [1, [4, [7, [3, [6, null]]]]]]], [[2, [5, [3, [1, [7, [4, [6, null]]]]]]], [[2, [5, [7, [4, [1, [3, [6, null]]]]]]], [[2, [6, [3, [7, [4, [1, [5, null]]]]]]], [[2, [7, [5, [3, [1, [6, [4, null]]]]]]], [[3, [1, [6, [2, [5, [7, [4, null]]]]]]], [[3, [1, [6, [4, [2, [7, [5, null]]]]]]], [[3, [5, [7, [2, [4, [6, [1, null]]]]]]], [[3, [6, [2, [5, [1, [4, [7, null]]]]]]], [[3, [7, [2, [4, [6, [1, [5, null]]]]]]], [[3, [7, [4, [1, [5, [2, [6, null]]]]]]], [[4, [1, [3, [6, [2, [7, [5, null]]]]]]], [[4, [1, [5, [2, [6, [3, [7, null]]]]]]], [[4, [2, [7, [5, [3, [1, [6, null]]]]]]], [[4, [6, [1, [3, [5, [7, [2, null]]]]]]], [[4, [7, [3, [6, [2, [5, [1, null]]]]]]], [[4, [7, [5, [2, [6, [1, [3, null]]]]]]], [[5, [1, [4, [7, [3, [6, [2, null]]]]]]], [[5, [1, [6, [4, [2, [7, [3, null]]]]]]], [[5, [2, [6, [3, [7, [4, [1, null]]]]]]], [[5, [3, [1, [6, [4, [2, [7, null]]]]]]], [[5, [7, [2, [4, [6, [1, [3, null]]]]]]], [[5, [7, [2, [6, [3, [1, [4, null]]]]]]], [[6, [1, [3, [5, [7, [2, [4, null]]]]]]], [[6, [2, [5, [1, [4, [7, [3, null]]]]]]], [[6, [3, [1, [4, [7, [5, [2, null]]]]]]], [[6, [3, [5, [7, [1, [4, [2, null]]]]]]], [[6, [3, [7, [4, [1, [5, [2, null]]]]]]], [[6, [4, [2, [7, [5, [3, [1, null]]]]]]], [[6, [4, [7, [1, [3, [5, [2, null]]]]]]], [[7, [2, [4, [6, [1, [3, [5, null]]]]]]], [[7, [3, [6, [2, [5, [1, [4, null]]]]]]], [[7, [4, [1, [5, [2, [6, [3, null]]]]]]], [[7, [5, [3, [1, [6, [4, [2, null]]]]]]], null]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
40
%