Hoán vị sao cho không có k + 2 điểm rơi vào bất kỳ đa thức bậc k


16

Sự miêu tả

Đặt một hoán vị của các số nguyên {1, 2, ..., n}được gọi là nội suy tối thiểu nếu không có tập hợp các k+2điểm (cùng với các chỉ số của chúng) rơi vào một đa thức bậc k. Đó là,

  1. Không có hai điểm rơi trên một đường nằm ngang (đa thức 0 độ)
  2. Không có ba điểm rơi trên một dòng (đa thức 1 độ)
  3. Không có bốn điểm rơi trên một parabol (đa thức 2 độ)
  4. Vân vân.

Thử thách

Viết chương trình tính toán trình tự OEIS A301802 (n) , số lượng hoán vị nội suy tối thiểu {1, 2, ..., n}cho ncàng lớn càng tốt.


Chấm điểm

Tôi sẽ tính thời gian mã của bạn trên máy tính của tôi (Intel Core i5 2,3 GHz, RAM 8 GB) với đầu vào tăng. Điểm của bạn sẽ là đầu vào lớn nhất chỉ mất chưa đến 1 phút để xuất giá trị chính xác.


Thí dụ

Ví dụ, hoán vị [1, 2, 4, 3]là nội suy tối thiểu bởi vì

the terms together with their indices 
[(1, 1), (2, 2), (3, 4), (4, 3)] 
have the property that
  (0) No two points have the same y-value.
  (1) No three points lie on a line.
  (2) No four points lie on a parabola.

Ví dụ minh họa rằng [1,2,4,3] có thể nội suy tối thiểu. Trong hình minh họa, bạn có thể thấy rằng các đường ngang (màu đỏ) có nhiều nhất một điểm trên chúng, các đường (màu xanh) có nhiều nhất hai điểm trên chúng và parabolas (màu xanh lá cây) có ba điểm trên chúng.


Dữ liệu

Dưới đây là các hoán vị tối thiểu interpolable cho n=3, n=4n=5:

n = 3: [1,3,2],[2,1,3],[2,3,1],[3,1,2]
n = 4: [1,2,4,3],[1,3,2,4],[1,3,4,2],[1,4,2,3],[2,1,3,4],[2,1,4,3],[2,3,1,4],[2,4,1,3],[2,4,3,1],[3,1,2,4],[3,1,4,2],[3,2,4,1],[3,4,1,2],[3,4,2,1],[4,1,3,2],[4,2,1,3],[4,2,3,1],[4,3,1,2]
n = 5: [1,2,5,3,4],[1,3,2,5,4],[1,3,4,2,5],[1,4,2,3,5],[1,4,3,5,2],[1,4,5,2,3],[1,4,5,3,2],[1,5,3,2,4],[2,1,4,3,5],[2,3,1,4,5],[2,3,5,1,4],[2,3,5,4,1],[2,4,1,5,3],[2,4,3,1,5],[2,4,5,1,3],[2,5,1,3,4],[2,5,1,4,3],[2,5,3,4,1],[2,5,4,1,3],[3,1,4,5,2],[3,1,5,2,4],[3,1,5,4,2],[3,2,5,1,4],[3,2,5,4,1],[3,4,1,2,5],[3,4,1,5,2],[3,5,1,2,4],[3,5,1,4,2],[3,5,2,1,4],[4,1,2,5,3],[4,1,3,2,5],[4,1,5,2,3],[4,1,5,3,2],[4,2,1,5,3],[4,2,3,5,1],[4,2,5,1,3],[4,3,1,2,5],[4,3,1,5,2],[4,3,5,2,1],[4,5,2,3,1],[5,1,3,4,2],[5,2,1,3,4],[5,2,1,4,3],[5,2,3,1,4],[5,2,4,3,1],[5,3,2,4,1],[5,3,4,1,2],[5,4,1,3,2]

Nếu chương trình của tôi đúng, một vài giá trị đầu tiên của a(n), số lượng hoán vị nội suy tối thiểu của {1, 2, ..., n}:

a(1) = 1
a(2) = 2
a(3) = 4
a(4) = 18
a(5) = 48
a(6) = 216
a(7) = 584
a(8) = 2870

Số thứ tự tốt đẹp! | Mặc dù bạn đã chỉ định mã nhanh nhất , bạn không chỉ định máy nào nhanh nhất. Chính xác các tiêu chí chiến thắng là gì?
dùng202729

3
Để thêm vào nhận xét của người dùng202729, tôi đề xuất một số thẻ mà bạn có thể sử dụng để xác định tiêu chí chiến thắng: mã nhanh nhất yêu cầu các bài nộp được kiểm tra trên cùng một máy để so sánh thời gian chạy (thường là OP của thử thách thực hiện điều này). thuật toán nhanh nhất sẽ yêu cầu người trả lời đưa ra mã với độ phức tạp thời gian thấp nhất có thể. code-golf sẽ yêu cầu người dùng đưa ra mã với mã nguồn ngắn nhất (hoặc tương đương) nhất có thể. Ngoài ra, đây thực sự là một thử thách tốt.
JungHwan Min

Văn bản ví dụ của bạn sử dụng lập chỉ mục bằng 0 mặc dù hình ảnh sử dụng lập chỉ mục một.
Jonathan Frech

Vì tất cả các điểm được xác định bởi hoán vị của các số tự nhiên đầu tiên, nên không có hai điểm nào có thể chiếm cùng một chiều cao?
Jonathan Frech

@JonathanFrech, thực sự, nó nên được lập chỉ mục 1 vì đây là những hoán vị. Và bạn đã đúng! Bởi vì chúng tôi đang xử lý các hoán vị, điều kiện đa thức 0 độ được cung cấp miễn phí.
Peter Kagey

Câu trả lời:


5

C #

using System;
using System.Diagnostics;
using BigInteger = System.Int32;

namespace Sandbox
{
    class PPCG160382
    {
        public static void Main(params string[] args)
        {
            if (args.Length != 0)
            {
                foreach (var arg in args) Console.WriteLine(CountValidPerms(int.Parse(arg)));
            }
            else
            {
                int[] smallValues = new int[] { 1, 1, 2, 4, 18, 48 };
                for (int n = 0; n < smallValues.Length; n++)
                {
                    var observed = CountValidPerms(n);
                    var expected = smallValues[n];
                    Console.WriteLine(observed == expected ? $"{n}: Ok" : $"{n}: expected {expected}, observed {observed}, error {observed - expected}");
                }
                for (int n = smallValues.Length; n < 13; n++)
                {
                    Stopwatch sw = new Stopwatch();
                    sw.Start();
                    Console.WriteLine($"{n}: {CountValidPerms(n)} in {sw.ElapsedMilliseconds}ms");
                }
            }
        }

        private static long CountValidPerms(int n)
        {
            // We work on the basis of exclusion by extrapolation.
            var unused = (1 << n) - 1;
            var excluded = new int[n];
            int[] perm = new int[n];

            // Symmetry exclusion: perm[0] < (n+1) / 2
            if (n > 1) excluded[0] = (1 << n) - (1 << ((n + 1) / 2));

            long count = 0;
            CountValidPerms(ref count, perm, 0, unused, excluded);
            return count;
        }

        private static void CountValidPerms(ref long count, int[] perm, int off, int unused, int[] excluded)
        {
            int n = perm.Length;
            if (off == n)
            {
                count += CountSymmetries(perm);
                return;
            }

            // Quick-aborts
            var completelyExcluded = excluded[off];
            for (int i = off + 1; i < n; i++)
            {
                if ((unused & ~excluded[i]) == 0) return;
                completelyExcluded &= excluded[i];
            }
            if ((unused & completelyExcluded) != 0) return;

            // Consider each unused non-excluded value as a candidate for perm[off]
            var candidates = unused & ~excluded[off];
            for (int val = 0; candidates > 0; val++, candidates >>= 1)
            {
                if ((candidates & 1) == 0) continue;

                perm[off] = val;

                var nextUnused = unused & ~(1 << val);

                var nextExcluded = (int[])excluded.Clone();
                // For each (non-trivial) subset of smaller indices, combine with off and extrapolate to off+1 ... excluded.Length-1
                if (off < n - 1 && off > 0)
                {
                    var points = new Point[off + 1];
                    var denoms = new BigInteger[off + 1];
                    points[0] = new Point { X = off, Y = perm[off] };
                    denoms[0] = 1;
                    ExtendExclusions(perm, off, 0, points, 1, denoms, nextExcluded);
                }

                // Symmetry exclusion: perm[0] < perm[-1] < n - 1 - perm[0]
                if (off == 0 && n > 1)
                {
                    nextExcluded[n - 1] |= (1 << n) - (2 << (n - 1 - val));
                    nextExcluded[n - 1] |= (2 << val) - 1;
                }

                CountValidPerms(ref count, perm, off + 1, nextUnused, nextExcluded);
            }
        }

        private static void ExtendExclusions(int[] perm, int off, int idx, Point[] points, int numPoints, BigInteger[] denoms, int[] excluded)
        {
            if (idx == off) return;

            // Subsets without
            ExtendExclusions(perm, off, idx + 1, points, numPoints, denoms, excluded);

            // Just add this to the subset
            points[numPoints] = new Point { X = idx, Y = perm[idx] };
            denoms = (BigInteger[])denoms.Clone();
            // Update invariant: denoms[s] = prod_{t != s} points[s].X - points[t].X
            denoms[numPoints] = 1;
            for (int s = 0; s < numPoints; s++)
            {
                denoms[s] *= points[s].X - points[numPoints].X;
                denoms[numPoints] *= points[numPoints].X - points[s].X;
            }
            numPoints++;

            for (int target = off + 1; target < excluded.Length; target++)
            {
                BigInteger prod = 1;
                for (int t = 0; t < numPoints; t++) prod *= target - points[t].X;

                Rational sum = new Rational(0, 1);
                for (int s = 0; s < numPoints; s++) sum += new Rational(prod / (target - points[s].X) * points[s].Y, denoms[s]);

                if (sum.Denom == 1 && sum.Num >= 0 && sum.Num < excluded.Length) excluded[target] |= 1 << (int)sum.Num;
            }

            // Subsets with
            ExtendExclusions(perm, off, idx + 1, points, numPoints, denoms, excluded);
        }

        private static int CountSymmetries(int[] perm)
        {
            if (perm.Length < 2) return 1;

            int cmp = 0;
            for (int i = 0, j = perm.Length - 1; i <= j; i++, j--)
            {
                cmp = perm.Length - 1 - perm[i] - perm[j];
                if (cmp != 0) break;
            }

            return cmp > 0 ? 4 : cmp == 0 ? 2 : 0;
        }

        public struct Point
        {
            public int X;
            public int Y;
        }

        public struct Rational
        {
            public Rational(BigInteger num, BigInteger denom)
            {
                if (denom == 0) throw new ArgumentOutOfRangeException(nameof(denom));

                if (denom < 0) { num = -num; denom = -denom; }

                var g = _Gcd(num, denom);
                Num = num / g;
                Denom = denom / g;
            }

            private static BigInteger _Gcd(BigInteger a, BigInteger b)
            {
                if (a < 0) a = -a;
                if (b < 0) b = -b;
                while (a != 0)
                {
                    var tmp = b % a;
                    b = a;
                    a = tmp;
                }
                return b;
            }

            public BigInteger Num;
            public BigInteger Denom;

            public static Rational operator +(Rational a, Rational b) => new Rational(a.Num * b.Denom + a.Denom * b.Num, a.Denom * b.Denom);
        }
    }
}

Lấy các giá trị ndưới dạng đối số dòng lệnh hoặc nếu chạy mà không có đối số lần lên đến n=10. Biên dịch thành "Phát hành" trong VS 2017 và chạy trên Intel Core i7-6700 tôi tính toán n=9trong 1,2 giây và n=10trong 13,6 giây. n=11chỉ hơn 2 phút.

FWIW:

n    a(n)
9    10408
10   45244
11   160248
12   762554
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.