Task: Latin Squares in reduced form


C++: #include <algorithm>
#include <functional>
#include <iostream>
#include <numeric>
#include <vector>

typedef std::vector<std::vector<int>> matrix;

matrix dList(int n, int start) {
    start--; 

    std::vector<int> a(n);
    std::iota(a.begin(), a.end(), 0);
    a[start] = a[0];
    a[0] = start;
    std::sort(a.begin() + 1, a.end());
    auto first = a[1];
    
    matrix r;
    std::function<void(int)> recurse;
    recurse = [&](int last) {
        if (last == first) {
            
            
            for (size_t j = 1; j < a.size(); j++) {
                auto v = a[j];
                if (j == v) {
                    return; 
                }
            }
            
            std::vector<int> b;
            std::transform(a.cbegin(), a.cend(), std::back_inserter(b), [](int v) { return v + 1; });
            r.push_back(b);
            return;
        }
        for (int i = last; i >= 1; i--) {
            std::swap(a[i], a[last]);
            recurse(last - 1);
            std::swap(a[i], a[last]);
        }
    };
    recurse(n - 1);
    return r;
}

void printSquare(const matrix &latin, int n) {
    for (auto &row : latin) {
        auto it = row.cbegin();
        auto end = row.cend();
        std::cout << '[';
        if (it != end) {
            std::cout << *it;
            it = std::next(it);
        }
        while (it != end) {
            std::cout << ", " << *it;
            it = std::next(it);
        }
        std::cout << "]\n";
    }
    std::cout << '\n';
}

unsigned long reducedLatinSquares(int n, bool echo) {
    if (n <= 0) {
        if (echo) {
            std::cout << "[]\n";
        }
        return 0;
    } else if (n == 1) {
        if (echo) {
            std::cout << "[1]\n";
        }
        return 1;
    }

    matrix rlatin;
    for (int i = 0; i < n; i++) {
        rlatin.push_back({});
        for (int j = 0; j < n; j++) {
            rlatin[i].push_back(j);
        }
    }
    
    for (int j = 0; j < n; j++) {
        rlatin[0][j] = j + 1;
    }

    unsigned long count = 0;
    std::function<void(int)> recurse;
    recurse = [&](int i) {
        auto rows = dList(n, i);

        for (size_t r = 0; r < rows.size(); r++) {
            rlatin[i - 1] = rows[r];
            for (int k = 0; k < i - 1; k++) {
                for (int j = 1; j < n; j++) {
                    if (rlatin[k][j] == rlatin[i - 1][j]) {
                        if (r < rows.size() - 1) {
                            goto outer;
                        }
                        if (i > 2) {
                            return;
                        }
                    }
                }
            }
            if (i < n) {
                recurse(i + 1);
            } else {
                count++;
                if (echo) {
                    printSquare(rlatin, n);
                }
            }
        outer: {}
        }
    };

    
    recurse(2);
    return count;
}

unsigned long factorial(unsigned long n) {
    if (n <= 0) return 1;
    unsigned long prod = 1;
    for (unsigned long i = 2; i <= n; i++) {
        prod *= i;
    }
    return prod;
}

int main() {
    std::cout << "The four reduced lating squares of order 4 are:\n";
    reducedLatinSquares(4, true);

    std::cout << "The size of the set of reduced latin squares for the following orders\n";
    std::cout << "and hence the total number of latin squares of these orders are:\n\n";
    for (int n = 1; n < 7; n++) {
        auto size = reducedLatinSquares(n, false);
        auto f = factorial(n - 1);
        f *= f * n * size;
        std::cout << "Order " << n << ": Size " << size << " x " << n << "! x " << (n - 1) << "! => Total " << f << '\n';
    }

    return 0;
}




Java: import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class LatinSquaresInReducedForm {

    public static void main(String[] args) {
        System.out.printf("Reduced latin squares of order 4:%n");
        for ( LatinSquare square : getReducedLatinSquares(4) ) {
            System.out.printf("%s%n", square);
        }
        
        System.out.printf("Compute the number of latin squares from count of reduced latin squares:%n(Reduced Latin Square Count) * n! * (n-1)! = Latin Square Count%n");
        for ( int n = 1 ; n <= 6 ; n++ ) {
            List<LatinSquare> list = getReducedLatinSquares(n);
            System.out.printf("Size = %d, %d * %d * %d = %,d%n", n, list.size(), fact(n), fact(n-1), list.size()*fact(n)*fact(n-1));
        }
    }
    
    private static long fact(int n) {
        if ( n == 0 ) {
            return 1;
        }
        int prod = 1;
        for ( int i = 1 ; i <= n ; i++ ) {
            prod *= i;
        }
        return prod;
    }
    
    private static List<LatinSquare> getReducedLatinSquares(int n) {
        List<LatinSquare> squares = new ArrayList<>();
        
        squares.add(new LatinSquare(n));
        PermutationGenerator permGen = new PermutationGenerator(n);
        for ( int fillRow = 1 ; fillRow < n ; fillRow++ ) {
            List<LatinSquare> squaresNext = new ArrayList<>();
            for ( LatinSquare square : squares ) {
                while ( permGen.hasMore() ) {
                    int[] perm = permGen.getNext();
                    
                    
                    if ( (perm[0]+1) != (fillRow+1) ) {
                        continue;
                    }
                    
                    
                    boolean permOk = true;
                    done:
                    for ( int row = 0 ; row < fillRow ; row++ ) {
                        for ( int col = 0 ; col < n ; col++ ) {
                            if ( square.get(row, col) == (perm[col]+1) ) {
                                permOk = false;
                                break done;
                            }
                        }
                    }
                    if ( permOk ) {
                        LatinSquare newSquare = new LatinSquare(square);
                        for ( int col = 0 ; col < n ; col++ ) {
                            newSquare.set(fillRow, col, perm[col]+1);
                        }
                        squaresNext.add(newSquare);
                    }
                }
                permGen.reset();
            }
            squares = squaresNext;
        }
        
        return squares;
    }
    
    @SuppressWarnings("unused")
    private static int[] display(int[] in) {
        int [] out = new int[in.length];
        for ( int i = 0 ; i < in.length ; i++ ) {
            out[i] = in[i] + 1;
        }
        return out;
    }
    
    private static class LatinSquare {
        
        int[][] square;
        int size;
        
        public LatinSquare(int n) {
            square = new int[n][n];
            size = n;
            for ( int col = 0 ; col < n ; col++ ) {
                set(0, col, col + 1);
            }
        }
        
        public LatinSquare(LatinSquare ls) {
            int n = ls.size;
            square = new int[n][n];
            size = n;
            for ( int row = 0 ; row < n ; row++ ) {
                for ( int col = 0 ; col < n ; col++ ) {
                    set(row, col, ls.get(row, col));
                }
            }
        }
        
        public void set(int row, int col, int value) {
            square[row][col] = value;
        }

        public int get(int row, int col) {
            return square[row][col];
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            for ( int row = 0 ; row < size ; row++ ) {
                sb.append(Arrays.toString(square[row]));
                sb.append("\n");
            }
            return sb.toString();
        }
        
        
    }

    private static class PermutationGenerator {

        private int[] a;
        private BigInteger numLeft;
        private BigInteger total;

        public PermutationGenerator (int n) {
            if (n < 1) {
                throw new IllegalArgumentException ("Min 1");
            }
            a = new int[n];
            total = getFactorial(n);
            reset();
        }

        private void reset () {
            for ( int i = 0 ; i < a.length ; i++ ) {
                a[i] = i;
            }
            numLeft = new BigInteger(total.toString());
        }

        public boolean hasMore() {
            return numLeft.compareTo(BigInteger.ZERO) == 1;
        }

        private static BigInteger getFactorial (int n) {
            BigInteger fact = BigInteger.ONE;
            for ( int i = n ; i > 1 ; i-- ) {
                fact = fact.multiply(new BigInteger(Integer.toString(i)));
            }
            return fact;
        }

        
        public int[] getNext() {
            if ( numLeft.equals(total) ) {
                numLeft = numLeft.subtract (BigInteger.ONE);
                return a;
            }

            
            int j = a.length - 2;
            while ( a[j] > a[j+1] ) {
                j--;
            }

            
            int k = a.length - 1;
            while ( a[j] > a[k] ) {
                k--;
            }

            
            int temp = a[k];
            a[k] = a[j];
            a[j] = temp;

            
            int r = a.length - 1;
            int s = j + 1;
            while (r > s) {
                int temp2 = a[s];
                a[s] = a[r];
                a[r] = temp2;
                r--;
                s++;
            }

            numLeft = numLeft.subtract(BigInteger.ONE);
            return a;
        }
    }

}