Trouvez les règles Golomb les plus courtes


15

Les règles de Golomb sont des ensembles d'entiers non négatifs de sorte qu'il n'y a pas deux paires d'entiers dans l'ensemble à la même distance.

Par exemple, [0, 1, 4, 6]est une règle de Golomb car toutes les distances entre deux entiers dans cet ensemble sont uniques:

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

Par souci de simplicité dans ce défi (et comme la traduction est triviale), nous imposons qu'une règle de Golomb contienne toujours le nombre0 (ce que fait l'exemple précédent).

Puisque cet ensemble est de longueur 4, nous disons qu'il s'agit d'une règle d' ordre de Golomb 4. La plus grande distance dans cet ensemble (ou élément, car il 0est toujours dans l'ensemble) est 6, donc nous disons qu'il s'agit d'une règle de Golomb de longueur 6 .

Ta tâche

Trouvez des règles d' ordre de Golomb 50à 100(inclus) qui ont des longueurs aussi petites que possible. Les règles que vous trouvez n'ont pas besoin d'être optimales (voir ci-dessous).

L'optimalité

Une règle d'ordre de Golomb Nest dite optimale s'il n'y a pas d'autre règle d'ordre de Golomb Nqui a une longueur plus petite.

Les règles optimales de Golomb sont connues pour les commandes inférieures à 28 , bien que trouver et prouver l'optimalité est de plus en plus difficile à mesure que la commande augmente.

Par conséquent, il n'est pas prévu que vous trouviez la règle de Golomb optimale pour l'un des ordres compris entre 50et 100(et encore moins attendu que vous puissiez prouver qu'ils sont optimaux).

Il n'y a pas de limite de temps dans l'exécution de votre programme.

Référence

La liste ci-dessous est la liste des longueurs de règles de Golomb de 50à 100(dans l'ordre) évaluées avec une stratégie de recherche naïve (Merci à @PeterTaylor pour cette liste):

[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 somme de toutes ces longueurs est 734078.

Notation

Votre score sera la somme des longueurs de tous vos chefs de Golomb entre 50et 100, divisé par la somme des longueurs des règles de Golomb entre 50et 100dans la ligne de base: 734078.

Si vous n'avez pas trouvé de règle de Golomb pour un ordre spécifique, vous devez calculer votre score de la même manière, en utilisant le double de la longueur dans la ligne de base pour cet ordre spécifique.

La réponse avec le score le plus bas l'emporte.

En cas d'égalité, les longueurs de l'ordre le plus élevé où les deux réponses diffèrent sont comparées et la plus courte l'emporte. Dans le cas où les deux réponses ont les mêmes longueurs pour toutes les commandes, la réponse qui a été publiée en premier l'emporte.


2
En relation. (Même défi en 2D.)
Martin Ender


Quand vous dites des règles entre 50 et 100, voulez-vous dire la plage [50, 100)? Donc, sans inclure la règle de l'ordre 100? Parce que la ligne de base ne contient que 50 éléments.
orlp

1
Remarque: la plus petite longueur possible d'une règle d'ordre de Golomb nest n(n-1)/2, car c'est le nombre de différences positives. Par conséquent, le score le plus petit possible dans ce défi est 147050/734078 > 0.2003193.
Greg Martin

2
@GregMartin Merci, bien que ce ne soit pas tout à fait le "plus petit score possible" mais plutôt une borne inférieure sur ce plus petit score possible!
Fatalize

Réponses:


8

C #, 259421/734078 ~ = 0,3534

Les méthodes

J'ai finalement trouvé une explication plus ou moins lisible de la méthode du champ projectif (méthode de Singer) dans Constructions of Generalized Sidon Sets , bien que je pense toujours qu'elle peut être légèrement améliorée. Elle s'avère être plus similaire à la méthode du champ affine (méthode de Bose) que les autres articles que j'ai lus l'avaient communiqué.

Cela suppose une connaissance des champs finis . Considérons que est une puissance première, et que F ( q ) soit notre champ de base.q=puneF(q)

La méthode du champ affine fonctionne sur . Prenons un générateur g 2 de F ( q 2 ) et un élément non nul k de F ( q ) et considérons l'ensemble { a : g 2F(q2)g2F(q2)kF(q) Ces valeurs forment une règle modulaire de Golomb mod q 2 - 1 . D'autres règles peuvent être obtenues en multipliant modulo q 2 - 1 par n'importe quel nombre qui est coprime avec le module.

{une:g2une-kg2Fq}
q2-1q2-1

La méthode du champ projectif fonctionne sur . Prenons un générateur g 3 de F ( q 3 ) et un élément non nul k de F ( q ) et considérons l'ensemble { 0 } { a : g 3F(q3)g3F(q3)kF(q) Ces valeurs forment une règle modulaire de Golomb mod q 2 + q + 1 . D'autres règles peuvent être obtenues par multiplication modulaire de la même manière que pour la méthode du champ affine.

{0}{une:g3une-kg3Fq}
q2+q+1

Notez que ces méthodes entre elles donnent les valeurs les plus connues pour chaque longueur supérieure à 16. Tomas Rokicki et Gil Dogon offrent une récompense de 250 $ à toute personne qui les bat pour des longueurs de 36 à 40000. Par conséquent, toute personne qui bat cette réponse est dans une monnaie prix.

Code

Le C # n'est pas très idiomatique, mais j'en ai besoin pour compiler avec une ancienne version de Mono. De plus, malgré la vérification des arguments, ce n'est pas un code de qualité de production. Je ne suis pas satisfait des types, mais je ne pense pas qu'il existe une très bonne solution à cela en C #. Peut-être en F # ou C ++ avec des modèles insensés.

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();
        }
    }
}

Résultats

Malheureusement, l'ajout des règles me prendrait environ 15k caractères au-delà de la limite de taille de publication, ils sont donc sur pastebin .


Seriez-vous si gentil de publier vos règles pour [50, 100] quelque part? J'ai un algorithme génétique que je veux essayer, en lui donnant quelques valeurs de graines.
orlp

@orlp, a ajouté un lien.
Peter Taylor

2
Comme je le soupçonnais, l'algorithme évolutif ne peut rien extraire de ces spécimens supérieurs. Même si au départ il semblait que les algorithmes évolutionnaires pouvaient fonctionner (il passe presque instantanément des règles invalides aux règles réelles), il y a trop de structure globale requise pour que l'algorithme évolutionnaire fonctionne.
orlp

5

Python 3, score 603001/734078 = 0,82144

Recherche naïve combinée à la construction Erdős – Turan:

2pk+(k2modp),k[0,p-1]

Pour les nombres premiers impairs p, cela donne une règle de golomb asymptotiquement optimale.

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))

Je ne pense pas que cette construction soit asymptotiquement optimale: elle donne une règle de Golomb d'ordre pet de longueur environ 2p^2, alors qu'il existe des règles de Golomb d'ordre net de longueur environ n^2asymptotiquement.
Greg Martin

@GregMartin Asymptotiquement, il n'y a pas de différence entre 2p^2et p^2.
orlp

Cela dépend de votre définition de «asymptotiquement», je suppose, mais pour moi, dans ce contexte, ils sont très différents.
Greg Martin

3

Mathematica, score 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 fonction ruzsaimplémente une construction d'une règle Golobm (également appelée ensemble Sidon) trouvée dans Imre Z. Ruzsa. Résolution d'une équation linéaire dans un ensemble d'entiers. I. Acta Arith., 65 (3): 259-282, 1993 . Étant donné n'importe quel nombre premier p, cette construction donne une règle de Golomb avec des p-1éléments contenus dans les entiers modulop(p-1) (c'est une condition encore plus forte que d'être une règle de Golomb dans les entiers eux-mêmes).

Un autre avantage du travail dans les entiers modulo mest que toute règle de Golomb peut être tournée (la même constante ajoutée à tous les éléments modulo m), et mise à l'échelle (tous les éléments multipliés par la même constante, tant que cette constante est relativement première m), et le résultat est toujours une règle de Golomb; parfois, le plus grand entier est considérablement diminué en procédant ainsi. La fonction scaledRuzsaessaie donc toutes ces échelles et enregistre les résultats. manyRuzsaSetscontient les résultats de cette construction et mise à l'échelle pour tous les 32 premiers nombres premiers (choisis un peu arbitrairement, mais le 32ème nombre premier, 131, est bien plus grand que 100); il y a près de 57 000 dirigeants Golomb dans cet ensemble, ce qui prend plusieurs bonnes minutes à calculer.

Bien sûr, les premiers kéléments d'une règle de Golomb forment eux-mêmes une règle de Golomb. Ainsi, la fonction tryGolombexamine une telle règle faite à partir de l'un des ensembles calculés ci-dessus. La dernière ligne Table...sélectionne la meilleure règle de Golomb possible, de chaque ordre de 50à 100, parmi toutes les règles de Golomb trouvées de cette manière.

Les longueurs trouvées étaient:

{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}

J'allais à l'origine combiner cela avec deux autres constructions, celles de Singer et de Bose; mais il semble que la réponse de Peter Taylor a déjà mis en œuvre cela, donc je suppose que je récupérerais simplement ces longueurs.


Je suis confus par votre affirmation selon laquelle en travaillant dans le module entier, mvous pouvez faire pivoter / mettre à l'échelle librement. Regardez le [0, 1, 4, 6]mod 7. Si j'ajoute 1, nous obtenons [0, 1, 2, 5], ce qui n'est pas une règle de Golomb.
orlp

C'est parce que vous devez commencer avec une règle de Golomb mod-7 pour que cela fonctionne. [0, 1, 4, 6]n'est pas une règle de Golomb mod-7 car elle 1 – 0est égale à 0 – 6modulo 7, par exemple.
Greg Martin

1
Pendant que j'écrivais et déboguais mon implémentation de champ fini en C #, j'aurais aimé mieux connaître Mathematica. Certainement l'une des bonnes langues pour le travail.
Peter Taylor
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.