Trova i sovrani Golomb più brevi


15

I righelli di Golomb sono insiemi di numeri interi non negativi tali che nessuna coppia di numeri interi nel gruppo si trova alla stessa distanza.

Ad esempio, [0, 1, 4, 6]è un righello Golomb perché tutte le distanze tra due numeri interi in questo set sono uniche:

0, 1 -> distance 1
0, 4 -> distance 4
0, 6 -> distance 6
1, 4 -> distance 3
1, 6 -> distance 5
4, 6 -> distance 2

Per semplicità in questa sfida (e poiché la traduzione è banale), imponiamo che un righello Golomb contenga sempre il numero0 (come nell'esempio precedente).

Poiché questo set è lungo 4, diciamo che questo è un sovrano dell'ordine del Golomb 4. La distanza maggiore in questo set (o elemento, poiché 0è sempre nel set) è 6, quindi diciamo che questo è un righello Golomb di lunghezza 6 .

Il tuo compito

Trova Golomb governanti di ordine 50 a 100(compreso) che hanno come piccole lunghezze come si può trovare. I righelli che trovi non devono essere ottimali (vedi sotto).

ottimalità

Un sovrano di Golomb N, si dice che sia ottimale se non c'è nessun altro sovrano di Golomb Nche abbia una lunghezza minore.

I sovrani Golomb ottimali sono noti per ordini inferiori a 28 , sebbene trovare e dimostrare l'ottimalità sia sempre più difficile man mano che l'ordine aumenta.

Pertanto, non ci si aspetta che tu trovi il righello Golomb ottimale per uno qualsiasi degli ordini tra 50e 100(e ancora meno previsto che tu possa dimostrare che sono ottimali).

Non ci sono limiti di tempo nell'esecuzione del programma.

Baseline

L'elenco seguente è l'elenco delle lunghezze dei righelli Golomb da 50a 100(in ordine) valutate con una strategia di ricerca ingenua (grazie a @PeterTaylor per questo elenco):

[4850 5122 5242 5297 5750 5997 6373 6800 6924 7459 7546 7788 8219 8502 8729 8941 9881 10199 10586 10897 11288 11613 11875 12033 12930 13393 14046 14533 14900 15165 15687 15971 16618 17354 17931 18844 19070 19630 19669 20721 21947 22525 23290 23563 23880 24595 24767 25630 26036 26254 27218]

La somma di tutte queste lunghezze è 734078.

punteggio

Il tuo punteggio sarà la somma delle lunghezze di tutti i vostri governanti Golomb tra 50e 100, diviso per la somma delle lunghezze dei governanti Golomb tra 50e 100nella linea di base: 734078.

Se non hai trovato un righello Golomb per un ordine specifico, devi calcolare il tuo punteggio allo stesso modo, usando il doppio della lunghezza nella linea di base per quell'ordine specifico.

Vince la risposta con il punteggio più basso.

In caso di pareggio, vengono confrontate le lunghezze del più grande ordine in cui le due risposte differiscono e vince quella più corta. Nel caso in cui entrambe le risposte abbiano le stesse lunghezze per tutti gli ordini, vince la risposta inviata per prima.


2
Relazionato. (Stessa sfida in 2D.)
Martin Ender il


Quando dici righelli tra 50 e 100, intendi l'intervallo [50, 100)? Quindi non includere il righello dell'ordine 100? Perché la linea di base contiene solo 50 elementi.
orlp

1
Nota a margine: la lunghezza minima possibile di un righello dell'ordine di Golomb nè n(n-1)/2, dal momento che è quante differenze positive ci sono. Pertanto il punteggio più piccolo possibile in questa sfida è 147050/734078 > 0.2003193.
Greg Martin,

2
@GregMartin Grazie, anche se questo non è proprio il "punteggio più piccolo possibile" ma piuttosto un limite inferiore al punteggio più piccolo possibile!
Fatalizza il

Risposte:


8

C #, 259421/734078 ~ = 0,3534

metodi

Alla fine ho trovato una spiegazione più o meno leggibile del metodo del campo proiettivo (metodo di Singer) in Costruzioni di insiemi di sidoni generalizzati , anche se penso ancora che possa essere leggermente migliorato. Risulta essere più simile al metodo del campo affine (metodo di Bose) rispetto agli altri documenti che ho letto avevano comunicato.

q=pun'F(q)

F(q2)g2F(q2)KF(q)

{un':g2un'-Kg2Fq}
q2-1q2-1

F(q3)g3F(q3)KF(q)

{0}{un':g3un'-Kg3Fq}
q2+q+1

Si noti che questi metodi tra loro forniscono i valori più noti per ogni lunghezza superiore a 16. Tomas Rokicki e Gil Dogon offrono una ricompensa di $ 250 per chiunque li batte per lunghezze da 36 a 40000. Pertanto, chiunque batte questa risposta è in cambio di una monetaria premio.

Codice

Il C # non è molto idiomatico, ma ne ho bisogno per compilare con una vecchia versione di Mono. Inoltre, nonostante la verifica dell'argomento, questo non è un codice di qualità della produzione. Non sono contento dei tipi, ma non credo che ci sia davvero una buona soluzione in C #. Forse in F # o C ++ con un modello folle.

using System;
using System.Collections.Generic;
using System.Linq;

namespace Sandbox {
    class Program {
        static void Main(string[] args) {
            var winners = ComputeRulerRange(50, 100);
            int total = 0;
            for (int i = 50; i <= 100; i++) {
                Console.WriteLine("{0}:\t{1}", i, winners[i][i - 1]);
                total += winners[i][i - 1];
            }
            Console.WriteLine("\t{0}", total);
        }

        static IDictionary<int, int[]> ComputeRulerRange(int min, int max) {
            var best = new Dictionary<int, int[]>();

            var naive = Naive(max);
            for (int i = min; i <= max; i++) best[i] = naive.Take(i).ToArray();

            var finiteFields = FiniteFields(max * 11 / 10).OrderBy(x => x.Size).ToArray();

            // The projective plane method generates rulers of length p^a + 1 for prime powers p^a.
            // We can then look at subrulers for a reasonable range, say down to two prime powers below.
            for (int ppi = 0; ppi < finiteFields.Length; ppi++) {
                // Range under consideration
                var field = finiteFields[ppi];
                int q = field.Size;
                int subFrom = Math.Max(min, ppi >= 2 ? finiteFields[ppi - 2].Size : 1);
                int subTo = Math.Min(max, q + 1);
                if (subTo < subFrom) continue;

                int m = q * q + q + 1;
                foreach (var ruler in ProjectiveRulers(field)) {
                    for (int sub = subFrom; sub <= subTo; sub++) {
                        var subruler = BestSubruler(ruler, sub, m);
                        if (subruler[sub - 1] < best[sub][sub - 1]) best[sub] = subruler;
                    }
                }
            }

            // Similarly for the affine plane method, which generates rulers of length p^a for prime powers p^a
            for (int ppi = 0; ppi < finiteFields.Length; ppi++) {
                // Range under consideration
                var field = finiteFields[ppi];
                int q = field.Size;
                int subFrom = Math.Max(min, ppi >= 2 ? finiteFields[ppi - 2].Size : 1);
                int subTo = Math.Min(max, q);
                if (subTo < subFrom) continue;

                int m = q * q - 1;
                foreach (var ruler in AffineRulers(field)) {
                    for (int sub = subFrom; sub <= subTo; sub++) {
                        var subruler = BestSubruler(ruler, sub, m);
                        if (subruler[sub - 1] < best[sub][sub - 1]) best[sub] = subruler;
                    }
                }
            }

            return best;
        }

        static int[] BestSubruler(int[] ruler, int sub, int m) {
            int[] expand = new int[ruler.Length + sub - 1];
            for (int i = 0; i < ruler.Length; i++) expand[i] = ruler[i];
            for (int i = 0; i < sub - 1; i++) expand[ruler.Length + i] = ruler[i] + m;

            int best = m, bestIdx = -1;
            for (int i = 0; i < ruler.Length; i++) {
                if (expand[i + sub - 1] - expand[i] < best) {
                    best = expand[i + sub - 1] - expand[i];
                    bestIdx = i;
                }
            }

            return expand.Skip(bestIdx).Take(sub).Select(x => x - ruler[bestIdx]).ToArray();
        }

        static IEnumerable<int[]> ProjectiveRulers(FiniteField field) {
            var q = field.Size;
            var fq3 = PowerField.Create(field, 3);
            var m = q * q + q + 1;
            var g = fq3.Generators.First();

            // Define the set T<k> = {0} \union {a \in [q^3-1] : g^a - kg \in F(q)} for 0 != k \in F(q)
            // This could alternatively be T<k> = {0} \union {log_g(b - kg) : b in F(q)} for 0 != k \in F(q)
            // Then T<k> % (q^2 + q + 1) gives a Golomb ruler.
            // For a given generator we seem to get the same ruler for every k.
            var t_k = new HashSet<int>();
            t_k.Add(0);
            var ga = fq3.One;
            for (int a = 1; a < fq3.Size; a++) {
                ga = ga * g;
                if (fq3.Convert(ga + g) < q) t_k.Add(a % m);
            }

            // TODO: optimise by detecting duplicates
            for (int s = 1; s < m; s++) {
                if (Gcd(s, m) == 1) yield return t_k.Select(x => x * s % m).OrderBy(x => x).ToArray();
            }
        }

        static IEnumerable<int[]> AffineRulers(FiniteField field) {
            var q = field.Size;
            var fq2 = PowerField.Create(field, 2);
            var m = q * q - 1;
            var g = fq2.Generators.First();

            // Define the set T<k> = {0} \union {a \in [q^2-1] : g^a - kg \in F(q)} for 0 != k \in F(q)
            // Then T<k> % (q^2 - 1) gives a Golomb ruler.
            var t_k = new HashSet<int>();
            var ga = fq2.One;
            for (int a = 1; a < fq2.Size; a++) {
                ga = ga * g;
                if (fq2.Convert(ga + g) < q) t_k.Add(a % m);
            }

            // TODO: optimise by detecting duplicates
            for (int s = 1; s < m; s++) {
                if (Gcd(s, m) == 1) yield return t_k.Select(x => x * s % m).OrderBy(x => x).ToArray();
            }
        }

        static int Gcd(int a, int b) {
            while (a != 0) {
                var t = b % a;
                b = a;
                a = t;
            }

            return b;
        }

        static int[] Naive(int size) {
            if (size == 0) return new int[0];
            if (size == 1) return new int[] { 0 };

            int[] ruler = new int[size];
            var diffs = new HashSet<int>();
            int i = 1, c = 1;
            while (true) {
                bool valid = true;
                for (int j = 0; j < i; j++) {
                    if (diffs.Contains(c - ruler[j])) { valid = false; break; }
                }

                if (valid) {
                    for (int j = 0; j < i; j++) diffs.Add(c - ruler[j]);
                    ruler[i++] = c;
                    if (i == size) return ruler;
                }

                c++;
            }
        }

        static IEnumerable<FiniteField> FiniteFields(int max) {
            bool[] isComposite = new bool[max + 1];
            for (int p = 2; p < isComposite.Length; p++) {
                if (!isComposite[p]) {
                     FiniteField baseField = new PrimeField(p); yield return baseField;
                    for (int pp = p * p, pow = 2; pp < max; pp *= p, pow++) yield return PowerField.Create(baseField, pow);
                    for (int pq = p * p; pq <= max; pq += p) isComposite[pq] = true;
                }
            }
        }
    }

    public abstract class FiniteField {
        private Lazy<FiniteFieldElement> _Zero;
        private Lazy<FiniteFieldElement> _One;

        public FiniteFieldElement Zero { get { return _Zero.Value; } }
        public FiniteFieldElement One { get { return _One.Value; } }
        public IEnumerable<FiniteFieldElement> Generators {
            get {
                for (int _g = 1; _g < Size; _g++) {
                    int pow = 0;
                    FiniteFieldElement g = Convert(_g), gpow = One;
                    while (true) {
                        pow++;
                        gpow = gpow * g;
                        if (gpow == One) break;
                        if (pow > Size) {
                            throw new Exception("Is this really a field? " + this);
                        }
                    }
                    if (pow == Size - 1) yield return g;
                }
            }
        }

        public abstract int Size { get; }
        internal abstract FiniteFieldElement Convert(int i);
        internal abstract int Convert(FiniteFieldElement f);

        internal abstract bool Eq(FiniteFieldElement a, FiniteFieldElement b);
        internal abstract FiniteFieldElement Negate(FiniteFieldElement a);
        internal abstract FiniteFieldElement Add(FiniteFieldElement a, FiniteFieldElement b);
        internal abstract FiniteFieldElement Mul(FiniteFieldElement a, FiniteFieldElement b);

        protected FiniteField() {
            _Zero = new Lazy<FiniteFieldElement>(() => Convert(0));
            _One = new Lazy<FiniteFieldElement>(() => Convert(1));
        }
    }

    public abstract class FiniteFieldElement {
        internal abstract FiniteField Field { get; }

        public static FiniteFieldElement operator -(FiniteFieldElement a) {
            return a.Field.Negate(a);
        }

        public static FiniteFieldElement operator +(FiniteFieldElement a, FiniteFieldElement b) {
            if (a.Field != b.Field) throw new ArgumentOutOfRangeException("b");
            return a.Field.Add(a, b);
        }

        public static FiniteFieldElement operator *(FiniteFieldElement a, FiniteFieldElement b) {
            if (a.Field != b.Field) throw new ArgumentOutOfRangeException("b");
            return a.Field.Mul(a, b);
        }

        public static bool operator ==(FiniteFieldElement a, FiniteFieldElement b) {
            if (Equals(a, null)) return Equals(b, null);
            else if (Equals(b, null)) return false;

            if (a.Field != b.Field) throw new ArgumentOutOfRangeException("b");
            return a.Field.Eq(a, b);
        }

        public static bool operator !=(FiniteFieldElement a, FiniteFieldElement b) { return !(a == b); }

        public override bool Equals(object obj) {
            return (obj is FiniteFieldElement) && (obj as FiniteFieldElement).Field == Field && this == (obj as FiniteFieldElement);
        }

        public override int GetHashCode() { return Field.Convert(this).GetHashCode(); }

        public override string ToString() { return Field.Convert(this).ToString(); }
    }

    public class PrimeField : FiniteField {
        private readonly int _Prime;
        private readonly PrimeFieldElement[] _Featherweight;

        internal int Prime { get { return _Prime; } }
        public override int Size { get { return _Prime; } }

        public PrimeField(int prime) {
            if (prime < 2) throw new ArgumentOutOfRangeException("prime");

            // TODO A primality test would be nice...

            _Prime = prime;
            _Featherweight = new PrimeFieldElement[Math.Min(prime, 256)];
        }

        internal override FiniteFieldElement Convert(int i) {
            if (i < 0 || i >= _Prime) throw new ArgumentOutOfRangeException("i");
            if (i >= _Featherweight.Length) return new PrimeFieldElement(this, i);
            if (Equals(_Featherweight[i], null)) _Featherweight[i] = new PrimeFieldElement(this, i);
            return _Featherweight[i];
        }

        internal override int Convert(FiniteFieldElement f) {
            if (f == null) throw new ArgumentNullException("f");
            if (f.Field != this) throw new ArgumentOutOfRangeException("f");

            return (f as PrimeFieldElement).Value;
        }

        internal override bool Eq(FiniteFieldElement a, FiniteFieldElement b) {
            if (a.Field != this) throw new ArgumentOutOfRangeException("a");
            if (b.Field != this) throw new ArgumentOutOfRangeException("b");

            return (a as PrimeFieldElement).Value == (b as PrimeFieldElement).Value;
        }

        internal override FiniteFieldElement Negate(FiniteFieldElement a) {
            if (a.Field != this) throw new ArgumentOutOfRangeException("a");
            var fa = a as PrimeFieldElement;
            return fa.Value == 0 ? fa : Convert(_Prime - fa.Value);
        }

        internal override FiniteFieldElement Add(FiniteFieldElement a, FiniteFieldElement b) {
            if (a.Field != this) throw new ArgumentOutOfRangeException("a");
            if (b.Field != this) throw new ArgumentOutOfRangeException("b");

            return Convert(((a as PrimeFieldElement).Value + (b as PrimeFieldElement).Value) % _Prime);
        }

        internal override FiniteFieldElement Mul(FiniteFieldElement a, FiniteFieldElement b) {
            if (a.Field != this) throw new ArgumentOutOfRangeException("a");
            if (b.Field != this) throw new ArgumentOutOfRangeException("b");

            return Convert(((a as PrimeFieldElement).Value * (b as PrimeFieldElement).Value) % _Prime);
        }

        public override string ToString() { return string.Format("F({0})", _Prime); }
    }

    internal class PrimeFieldElement : FiniteFieldElement {
        private readonly PrimeField _Field;
        private readonly int _Value;

        internal override FiniteField Field { get { return _Field; } }
        internal int Value { get { return _Value; } }

        internal PrimeFieldElement(PrimeField field, int val) {
            if (field == null) throw new ArgumentNullException("field");
            if (val < 0 || val >= field.Prime) throw new ArgumentOutOfRangeException("val");

            _Field = field;
            _Value = val;
        }
    }

    public class PowerField : FiniteField {
        private readonly FiniteField _BaseField;
        private readonly FiniteFieldElement[] _Polynomial;

        internal FiniteField BaseField { get { return _BaseField; } }
        internal int Power { get { return _Polynomial.Length; } }
        public override int Size { get { return (int)Math.Pow(_BaseField.Size, Power); } }

        public PowerField(FiniteField baseField, FiniteFieldElement[] polynomial) {
            if (baseField == null) throw new ArgumentNullException("baseField");
            if (polynomial == null) throw new ArgumentNullException("polynomial");
            if (polynomial.Length < 2) throw new ArgumentOutOfRangeException("polynomial");
            for (int i = 0; i < polynomial.Length; i++) if (polynomial[i].Field != baseField) throw new ArgumentOutOfRangeException("polynomial[" + i + "]");

            // TODO Check that the polynomial is irreducible over the base field.

            _BaseField = baseField;
            _Polynomial = polynomial.ToArray();
        }

        internal override FiniteFieldElement Convert(int i) {
            if (i < 0 || i >= Size) throw new ArgumentOutOfRangeException("i");

            var vec = new FiniteFieldElement[Power];
            for (int j = 0; j < vec.Length; j++) {
                vec[j] = BaseField.Convert(i % BaseField.Size);
                i /= BaseField.Size;
            }

            return new PowerFieldElement(this, vec);
        }

        internal override int Convert(FiniteFieldElement f) {
            if (f == null) throw new ArgumentNullException("f");
            if (f.Field != this) throw new ArgumentOutOfRangeException("f");

            var pf = f as PowerFieldElement;
            int i = 0;
            for (int j = Power - 1; j >= 0; j--) i = i * BaseField.Size + BaseField.Convert(pf.Value[j]);
            return i;
        }

        internal override bool Eq(FiniteFieldElement a, FiniteFieldElement b) {
            if (a.Field != this) throw new ArgumentOutOfRangeException("a");
            if (b.Field != this) throw new ArgumentOutOfRangeException("b");

            var fa = a as PowerFieldElement;
            var fb = b as PowerFieldElement;
            for (int i = 0; i < Power; i++) if (fa.Value[i] != fb.Value[i]) return false;
            return true;
        }

        internal override FiniteFieldElement Negate(FiniteFieldElement a) {
            if (a.Field != this) throw new ArgumentOutOfRangeException("a");
            return new PowerFieldElement(this, (a as PowerFieldElement).Value.Select(x => -x).ToArray());
        }

        internal override FiniteFieldElement Add(FiniteFieldElement a, FiniteFieldElement b) {
            if (a.Field != this) throw new ArgumentOutOfRangeException("a");
            if (b.Field != this) throw new ArgumentOutOfRangeException("b");

            var fa = a as PowerFieldElement;
            var fb = b as PowerFieldElement;
            var vec = new FiniteFieldElement[Power];
            for (int i = 0; i < Power; i++) vec[i] = fa.Value[i] + fb.Value[i];
            return new PowerFieldElement(this, vec);
        }

        internal override FiniteFieldElement Mul(FiniteFieldElement a, FiniteFieldElement b) {
            if (a.Field != this) throw new ArgumentOutOfRangeException("a");
            if (b.Field != this) throw new ArgumentOutOfRangeException("b");

            var fa = a as PowerFieldElement;
            var fb = b as PowerFieldElement;

            // We consider fa and fb as polynomials of a variable x and multiply modulo (x^Power - _Polynomial).
            // But to keep things simple we want to manage the cascading modulo.
            var vec = Enumerable.Repeat(BaseField.Zero, Power).ToArray();
            var fa_xi = fa.Value.ToArray();
            for (int i = 0; i < Power; i++) {
                for (int j = 0; j < Power; j++) vec[j] += fb.Value[i] * fa_xi[j];
                if (i < Power - 1) ShiftLeft(fa_xi);
            }

            return new PowerFieldElement(this, vec);
        }

        private void ShiftLeft(FiniteFieldElement[] vec) {
            FiniteFieldElement head = vec[vec.Length - 1];
            for (int i = vec.Length - 1; i > 0; i--) vec[i] = vec[i - 1] + head * _Polynomial[i];
            vec[0] = head * _Polynomial[0];
        }

        public static FiniteField Create(FiniteField baseField, int power) {
            if (baseField == null) throw new ArgumentNullException("baseField");
            if (power < 2) throw new ArgumentOutOfRangeException("power");

            // Since the field is cyclic, there is only one finite field of a given prime power order (up to isomorphism).
            // For most practical purposes that means that we can pick any arbitrary monic irreducible polynomial.
            // We can abuse PowerField to do polynomial multiplication in the base field.
            var fakeField = new PowerField(baseField, Enumerable.Repeat(baseField.Zero, power).ToArray());
            var excluded = new HashSet<FiniteFieldElement>();
            for (int lpow = 1; lpow <= power / 2; lpow++) {
                int upow = power - lpow;
                // Consider all products of a monic polynomial of order lpow with a monic polynomial of order upow.
                int xl = (int)Math.Pow(baseField.Size, lpow);
                int xu = (int)Math.Pow(baseField.Size, upow);
                for (int i = xl; i < 2 * xl; i++) {
                    var pi = fakeField.Convert(i);
                    for (int j = xu; j < 2 * xu; j++) {
                        var pj = fakeField.Convert(j);
                        excluded.Add(-(pi * pj));
                    }
                }
            }

            for (int p = baseField.Size; true; p++) {
                var pp = fakeField.Convert(p) as PowerFieldElement;
                if (!excluded.Contains(pp)) return new PowerField(baseField, pp.Value.ToArray());
            }
        }

        public override string ToString() {
            var sb = new System.Text.StringBuilder();
            sb.AppendFormat("GF({0}) with primitive polynomial x^{1} ", Size, Power);
            for (int i = Power - 1; i >= 0; i--) sb.AppendFormat("+ {0}x^{1}", _Polynomial[i], i);
            sb.AppendFormat(" over base field ");
            sb.Append(_BaseField);
            return sb.ToString();
        }
    }

    internal class PowerFieldElement : FiniteFieldElement {
        private readonly PowerField _Field;
        private readonly FiniteFieldElement[] _Vector; // The version of Mono I have doesn't include IReadOnlyList<T>

        internal override FiniteField Field { get { return _Field; } }
        internal FiniteFieldElement[] Value { get { return _Vector; } }

        internal PowerFieldElement(PowerField field, params FiniteFieldElement[] vector) {
            if (field == null) throw new ArgumentNullException("field");
            if (vector == null) throw new ArgumentNullException("vector");
            if (vector.Length != field.Power) throw new ArgumentOutOfRangeException("vector");
            for (int i = 0; i < vector.Length; i++) if (vector[i].Field != field.BaseField) throw new ArgumentOutOfRangeException("vector[" + i + "]");

            _Field = field;
            _Vector = vector.ToArray();
        }
    }
}

risultati

Sfortunatamente aggiungere i righelli mi porterebbe circa 15k caratteri oltre il limite della dimensione del post, quindi sono su pastebin .


Saresti così gentile da pubblicare i tuoi sovrani per [50, 100] da qualche parte? Ho un algoritmo genetico che voglio provare, alimentandolo con alcuni valori di base.
orlp

@orlp, ha aggiunto un collegamento.
Peter Taylor,

2
Come sospettavo, l'algoritmo evolutivo non può estrarre nulla di utile da questi campioni superiori. Anche se inizialmente sembrava che gli algoritmi evolutivi potessero funzionare (si sposta praticamente istantaneamente da righelli non validi a righelli reali), è necessaria troppa struttura globale affinché l'algoritmo evolutivo funzioni.
orlp

5

Python 3, punteggio 603001/734078 = 0,82144

Ricerca ingenua combinata con la costruzione Erdős – Turan:

2pK+(K2modp),K[0,p-1]

Per i numeri dispari p questo dà un righello golomb asintoticamente ottimale.

def isprime(n):
    if n < 2: return False
    if n % 2 == 0: return n == 2
    k = 3
    while k*k <= n:
         if n % k == 0: return False
         k += 2
    return True

rulers = []
ruler = []
d = set()
n = 0
while len(ruler) <= 100:
    order = len(ruler) + 1
    if order > 2 and isprime(order):
        ruler = [2*order*k + k*k%order for k in range(order)]
        d = {a-b for a in ruler for b in ruler if a > b}
        n = max(ruler) + 1
        rulers.append(tuple(ruler))
        continue

    nd = set(n-e for e in ruler)
    if not d & nd:
        ruler.append(n)
        d |= nd
        rulers.append(tuple(ruler))
    n += 1


isuniq = lambda l: len(l) == len(set(l))
isruler = lambda l: isuniq([a-b for a in l for b in l if a > b])

assert all(isruler(r) for r in rulers)

rulers = list(sorted([r for r in rulers if 50 <= len(r) <= 100], key=len))
print(sum(max(r) for r in rulers))

Non penso che questa costruzione sia asintoticamente ottimale: produce un sovrano Golomb di ordine pe lunghezza circa 2p^2, mentre esistono sovrani Golomb di ordine ne lunghezza circa n^2asintoticamente.
Greg Martin,

@GregMartin Asintoticamente non c'è differenza tra 2p^2e p^2.
orlp

Dipende dalla tua definizione di "asintoticamente", immagino, ma per me, in questo contesto, sono molto diversi.
Greg Martin,

3

Mathematica, punteggio 276235/734078 <0,376302

ruzsa[p_, i_] := Module[{g = PrimitiveRoot[p]},
  Table[ChineseRemainder[{t, i PowerMod[g, t, p]}, {p - 1, p}], {t, 1, p - 1}] ]

reducedResidues[m_] := Select[Range@m, CoprimeQ[m, #] &]

rotate[set_, m_] := Mod[set - #, m] & /@ set

scaledRuzsa[p_] := Union @@ Table[ Sort@Mod[a b, p (p - 1)],
  {a, reducedResidues[p (p - 1)]}, {b, rotate[ruzsa[p, 1], p (p - 1)]}]

manyRuzsaSets = Join @@ Table[scaledRuzsa[Prime[n]], {n, 32}];

tryGolomb[set_, k_] := If[Length[set] < k, Nothing, Take[set, k]]

Table[First@MinimalBy[tryGolomb[#, k] & /@ manyRuzsaSets, Max], {k, 50, 100}]

La funzione ruzsaimplementa una costruzione di un righello Golobm (chiamato anche set di Sidone) trovato in Imre Z. Ruzsa. Risolvere un'equazione lineare in un insieme di numeri interi. I. Acta Arith., 65 (3): 259-282, 1993 . Dato qualsiasi numero primo p, questa costruzione produce un righello Golomb con p-1elementi contenuti nel modulo interi p(p-1)(questa è una condizione ancora più forte dell'essere un righello Golomb negli interi stessi).

Un altro vantaggio di lavorare nel modulo intero mè che qualsiasi righello Golomb può essere ruotato (la stessa costante aggiunta a tutti gli elementi modulo m), e ridimensionato (tutti gli elementi moltiplicati per la stessa costante, purché tale costante sia relativamente adescata m), e il risultato è ancora un sovrano Golomb; a volte il numero intero più grande viene ridotto in modo significativo in questo modo. Quindi la funzione scaledRuzsaprova tutti questi ridimensionamenti e registra i risultati. manyRuzsaSetscontiene i risultati di questa costruzione e ridimensionamento per tutti i primi 32 numeri primi (scelti un po 'arbitrariamente, ma il 32 ° primo, 131, è ben più grande di 100); ci sono quasi 57.000 righelli Golomb in questo set, che richiede diversi minuti per essere calcolati.

Naturalmente, i primi kelementi di un sovrano Golomb formano essi stessi un sovrano Golomb. Quindi la funzione tryGolombguarda un tale righello ricavato da uno dei set sopra calcolati. L'ultima riga Table...seleziona il miglior sovrano Golomb che può, di ogni ordine da 50a 100, da tutti i governanti Golomb trovano in questo modo.

Le lunghezze trovate sono state:

{2241, 2325, 2399, 2578, 2640, 2762, 2833, 2961, 3071, 3151, 3194, 3480, 3533, 3612, 3775, 3917, 4038, 4150, 4237, 4368, 4481, 4563, 4729, 4974, 5111, 5155, 5297, 5504, 5583, 5707, 5839, 6077, 6229, 6480, 6611, 6672, 6913, 6946, 7025, 7694, 7757, 7812, 7969, 8139, 8346, 8407, 8678, 8693, 9028, 9215, 9336}

Inizialmente avrei combinato questo con altre due costruzioni, quelle di Singer e di Bose; ma sembra che la risposta di Peter Taylor lo abbia già implementato, quindi presumibilmente avrei semplicemente recuperato quelle lunghezze.


Sono confuso dalla tua affermazione che lavorando nel modulo intero mpuoi ruotare / ridimensionare liberamente. Guarda la [0, 1, 4, 6]mod 7. Se aggiungo 1 otteniamo [0, 1, 2, 5], che non è un righello Golomb.
orlp

Questo perché devi iniziare con un righello Golomb mod-7 affinché funzioni. [0, 1, 4, 6]non è un righello Golomb mod-7 perché è 1 – 0uguale a 0 – 6modulo 7, per esempio.
Greg Martin,

1
Mentre stavo scrivendo e eseguendo il debug della mia implementazione sul campo finito in C #, avrei desiderato conoscere meglio Mathematica. Sicuramente una delle lingue giuste per il lavoro.
Peter Taylor,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.