Suche nach kürzeren Alternativen zu `range (…)`

8

Die beste Lösung, die ich bisher für ein Golf-Code-Puzzle gefunden habe, an dem ich arbeite, sind zwei ziemlich fett aussehende Aufrufe von range. Ich bin sehr neu im Code Golf, besonders in Python, also könnte ich ein paar Tipps gebrauchen.

Das relevante Fragment ist dies

[x for x in range(n+1,7**6)if`x`==`x`[::-1]*all(x%i for i in range(2,x))]

Die Obergrenze der ersten rangeist nicht scharf. Es sollte mindestens 98690 sein, und alles andere ist gleich ( dh golfmäßig). Je kleiner der Unterschied zwischen dieser Obergrenze und 98690 ist, desto besser ist die Leistung 1 . Ich verwende 7 6 (= 117649), weil dies 7**6der kürzeste Python-Ausdruck ist, den ich mir vorstellen kann.

Im Gegensatz dazu sind die Untergrenze in der ersten rangesowie beide Grenzen in der zweiten fest. IOW, das Programm (in seiner aktuellen Form) führt zu falschen Ergebnissen, wenn diese Grenzwerte geändert werden.

Gibt es eine Möglichkeit, einen oder beide Ausdrücke zu verkürzen?

range(n+1,7**6)
range(2,x)

?

Übrigens, in diesem Fall gewinnt das Aliasing rangevon beispielsweise rnichts:

r=range;rr
rangerange

EDIT: FWIW, das vollständige Programm ist das folgende:

p=lambda n:[x for x in range(n+1,7**6)if`x`==`x`[::-1]*all(x%i for i in range(2,x))][0]

p(n)sollte die kleinste palindromische Primzahl größer sein als n. Auch psollte nicht rekursiv sein. Achtung: Es ist schon obszön langsam!


1 Ja, ich weiß: Leistung ist beim Code-Golf irrelevant, aber deshalb habe ich geschrieben, dass "alles andere gleich ist (was das Golf betrifft)". Zum Beispiel meine Wahl 7**6und nicht die unmittelbar offensichtliche, aber leistungsschwächere "Golf-Äquivalent" -Alternative 9**9. Ich mag es , meine Code-Golf-Versuche tatsächlich auszuführen , was bedeutet, dass die Leistung nicht so stark beeinträchtigt wird, dass es Jahre dauern würde, bis der Code ausgeführt wird. Wenn ich helfen kann, natürlich.

kjo
quelle
1
fwiw, Sie könnten Ihre zu Testzwecken viel schneller machen, indem Sie Generatoren verwenden; es ist äquivalent, außer dass : p=lambda n:(x for x in xrange(n+1,7**6)if`x`==`x`[::-1]*all(x%i for i in xrange(2,x))).next(). Während Sie dabei sind, können Sie natürlich genauso gut xrange(2,x)zu xrange(2,int(x**.5+1))Ihren Tests wechseln und diese sehr schnell durchführen. Dieser Code entspricht eindeutig Ihrem Code, nur länger und schneller.
Justin
1
Es ist unmöglich, viele gute Golf-Tricks mit einem kurzen Fragment zu entwickeln, das von seinem Kontext isoliert ist. Die besten Golfprogramme stellen oft überraschende Verbindungen zwischen verschiedenen Teilen der Programme her. Zum Beispiel kann sich eine scheinbar nutzlose verworfene Variable als Schlüssel erweisen oder zwei nicht verwandte Schleifen, die zu einer zusammengefasst sind.
Feersum
@feersum: Ich habe das Ganze vor einiger Zeit (in einer EDIT) gepostet. Es war, bevor Sie Ihren Kommentar gepostet haben. Hast du es nicht gesehen?
Kjo
@FryAmTheEggman: Ja, die Aussage des Puzzles ist, dass es eine Funktion sein muss, und was noch schlimmer ist, die Funktion kann nicht rekursiv sein.
Kjo
1
Vielleicht möchten Sie hier
Beta Decay

Antworten:

7

Machen Sie es zu einer einzigen Schleife

Wie es ist, haben Sie zwei Schleifen: eine, die über xpalindromische Primzahlen iteriert, eine andere, die iteriert, um izu überprüfen, ob xes sich um eine Primzahl handelt, die durch die Testteilung primiert wird. Wie Sie bemerkt haben, benötigt Python in vielen Schleifen viele Zeichen, oft zum Schreiben range, aber auch zum Schreiben while _:oder for x in _. Eine Golf-Python-Lösung sollte sich also Mühe geben, so wenig Loops wie möglich zu verwenden.

Der Kommentar von feersum "Die besten Golfprogramme stellen oft überraschende Verbindungen zwischen verschiedenen Teilen der Programme her" ist hier sehr zutreffend. Die Primprüfung scheint wie eine separate Unterroutine zu sein, für die all(x%i for i in range(2,x))der klassische Ausdruck gilt. Aber wir machen es anders.

Die Idee ist, Wilsons Theorem zu verwenden . Für jede potenzielle Primzahl kbehalten wir ein laufendes Produkt von (k-1)!bei und prüfen, ob es ein Vielfaches von ist k. Wir können den Überblick behalten, (k-1)!während wir das Potenzial testen k, erstklassige Palindrome zu sein, indem wir ein laufendes Produkt behalten P.

Eigentlich werden wir die stärkere Version von Wilsons Satz verwenden , die (k-1)! % k0 für Verbunde gleich kund nur für zusammengesetzte Zahlen, mit Ausnahme k=4gibt 2, und so (k-1)!**2 % kentspricht 0genau für zusammengesetzte Zahlen. Wir werden über das Update Pauf gleich k!**2aktualisieren P*=k*k.

(Siehe diese Antwort für diese Methode zum Suchen von Primzahlen in Python.)

Alles zusammen:

def p(n):
 k=P=1
 while(`k`!=`k`[::-1])+(k<=n)+(P%k==0):P*=k*k;k+=1
 return k

Dies ist noch nicht vollständig golfen - insbesondere die Bedingung ist ineffizient geschrieben. Wir können die Bedingung komprimieren, um zu überprüfen, ob kes sich um ein Palindrom handelt, während wir gleichzeitig die anderen Bedingungen über eine verkettete Ungleichung erzwingen.

def p(n):
 k=P=1
 while`k`*(P%k>0>n-k)!=`k`[::-1]:P*=k*k;k+=1
 return k
xnor
quelle
1
eine schillernde Lösung. Ich bin dankbar, es zu sehen, auch wenn es in einen Bereich geht, den ich nicht erreichen kann ... Bis jetzt ging es bei meinen Verbesserungen im Golf nur darum, Tricks wie die Verwendung von Backticks zu erlernen str, aber diese Tricks bekommen nur einen viel ... die großen Verbesserungen kommen wie immer von besseren Algorithmen.
Kjo
1
Nach der Lösung von FryAmTheEggman kann man den Zustand wie folgt umschreiben `k`*(k>n)*(P%k>0)!=`k`[::-1], der sich
Uhr
1
@kjo Ja, und noch ähnlicher, wenn Sie die Ungleichungen zu einer einzigen verketten. Sie sollten auch Python Golf Practice
ausprobieren, um solche
das ist sehr schön gemacht! Danke euch allen! Dies war für mich ein sehr aufschlussreicher Thread ... Die kürzesten mir bekannten Lösungslängen für Python 2.7 und 3.3 sind 28 bzw. 32 Zeichen kürzer als die Version, die ich ursprünglich veröffentlicht habe (meine absolut beste Anstrengung), und als ich davon erfuhr, war ich ungläubig; Ich dachte, irgendwo müsste es ein schlechtes Spiel geben (z. B. durch Reverse Engineering des automatisierten Lösungstestprogramms). Aber nachdem ich gesehen habe, wie die Experten hier nach besten Kräften schnell bis zu 16 Zeichen rasiert haben, bin ich jetzt eher bereit, diesen Zahlen zu glauben.
Kjo
@kjo Ich bin froh, dass du die Magie des Golfsports siehst. Wollen Sie damit sagen, dass es eine 55-Zeichen-Lösung gibt? Wenn ja, bin ich fasziniert. Ist das ein Anarchie-Golfproblem? Könnten Sie mich bitte mit der Problemstellung verknüpfen? Möglicherweise sind Verknüpfungen aufgrund von Lücken in den Testfällen möglich, insbesondere die Ausnahme mit der Nummer 4.
xnor
3

AFAIK, nicht wirklich.

Die Reichweite wird häufig in Python-Golfspielen verwendet, da dies der kürzeste Weg ist, um Listen mit zunehmenden / abnehmenden Zahlen zu erstellen.

Das heißt, es scheint etwas (7 Bytes) kürzer zu sein, um die Verwendung von range zu vermeiden und stattdessen eine umschlossene while-Schleife aufzurufen:

def p(n):
    n+=1
    while`n`*all(n%i for i in range(2,n))!=`n`[::-1]:n+=1
    return n

Vielen Dank an @xnor (wie immer) für die Verbesserung der Logik der while-Bedingung :)

FryAmTheEggman
quelle
@kjo Kein Problem :) Vielleicht möchten Sie der Frage hinzufügen, was Sie mir darüber gesagt haben, dass es eine nicht rekursive Funktion sein muss, da diese Antwort sonst eher schlecht ist;)
FryAmTheEggman
2
Sie können die Bedingung speichern, indem Sie sie implizit negieren : while(`n`!=`n`[::-1])+0in(n%i for i in range(2,n)):n+=1. Ich kann das erste Paar von Parens aufgrund von Problemen mit der Priorität des Bedieners nicht loswerden.
xnor
2
Die Parens loswerden:while`n`*all(n%i for i in range(2,n))!=`n`[::-1]:n+=1
xnor
@FryAmTheEggman: das ist sehr sportlich von dir! erledigt
kjo
1

Wenn Sie iterative Algorithmen wie die Newton-Methode oder die Berechnung von Fraktalen verwenden, bei denen Sie normalerweise Iterationen durchführen müssen , ohne sich um den Index zu kümmern , können Sie einige Zeichen speichern, indem Sie stattdessen über kurze Zeichenfolgen iterieren.

for i in range(4):x=f(x)
for i in'asdf':x=f(x)

Bei sieben Iterationen ist dies ausgeglichen range. Verwenden Sie für weitere Iterationen Backtics und große Zahlen

for i in`9**9**5`:pass

Dies läuft 56349 Mal, was für alle praktischen Zwecke ausreichen sollte. Wenn Sie mit Zahlen und Operatoren herumspielen, können Sie auf diese Weise eine Vielzahl von Zahlen fest codieren.

DenDenDo
quelle
Obwohl dies interessant ist, können Sie ziemlich deutlich sehen, dass er sich um den Index kümmert (da der Kandidatenprimus der Index ist). Ich denke, das passt besser als Teil dieser Antwort auf der Seite mit den Tipps :) (Beachten Sie auch, dass dies '1'*4kürzer ist als 'asdf')
FryAmTheEggman