Procurando alternativas mais curtas para `range (…)`

8

A melhor solução que encontrei até agora para um quebra-cabeça de código de golfe em que estou trabalhando inclui duas invocações bastante gordasrange . Sou muito novo em código de golfe, especialmente em Python, para poder usar algumas dicas.

O fragmento relevante é este

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

O limite superior do primeiro rangenão é nítido. Deveria ser pelo menos 98690, e todo o resto ser igual (ou seja, em termos de golfe), quanto menor a diferença entre esse limite superior e 98690, melhor e em termos de desempenho 1 . Estou usando 7 6 (= 117649) porque 7**6é a expressão Python mais curta que eu posso apresentar que se encaixa na conta.

Por outro lado, o limite inferior no primeiro rangee os dois no segundo são firmes. IOW, o programa (em sua forma atual) produzirá resultados incorretos se esses limites forem alterados.

Existe uma maneira de encurtar uma ou ambas as expressões

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

?

Aliás, neste caso, aliasing range, digamos, rnão ganha nada:

r=range;rr
rangerange

EDIT: FWIW, o programa completo é o seguinte:

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)deve ser o menor primo palindrômico maior que n. Além disso, pnão deve ser recursivo. Aviso: já é obscenamente lento!


1 Sim, eu sei: o desempenho é irrelevante no código de golfe, mas foi por isso que escrevi "todo o resto é igual (em termos de golfe)". Por exemplo, minha escolha de 7**6, e não a alternativa mais imediatamente óbvia, mas com pior desempenho e "equivalente ao golfe" 9**9. Eu realmente gosto de executar minhas tentativas de código de golfe, o que significa não deixar o desempenho degradar a ponto de levar anos para executar o código. Se eu puder ajudar, é claro.

kjo
fonte
1
fwiw, você pode tornar o seu muito mais rápido para fins de teste usando geradores; é equivalente exceto para que: 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(). Claro que, quando seu isso, poderia muito bem mudar xrange(2,x)para xrange(2,int(x**.5+1))e fazer o seu teste muito rápido. Claramente, esse código é equivalente ao seu, apenas mais longo e mais rápido.
Justin
1
É impossível inventar muitos bons truques de golfe com um pequeno fragmento isolado de seu contexto. Os programas com melhor desempenho de golfe geralmente fazem conexões surpreendentes entre diferentes partes dos programas. Por exemplo, uma variável descartada aparentemente inútil pode ser a chave ou dois loops não relacionados combinados em um.
feersum
@feersum: Eu postei a coisa toda (em uma edição) há um bom tempo atrás. Foi antes de você postar seu comentário. Você não viu?
KJo
@FryAmTheEggman: sim, a afirmação do quebra-cabeça é que ele deve ser uma função e, o que é pior, a função não pode ser recursiva.
KJo
1
Você pode querer olhar aqui
Decay Beta 24/11

Respostas:

7

Torná-lo um loop único

Como é, você tem dois loops: um repetindo xque pode ser primos palindrômicos, outro repetindo ipara verificar se xé primo pela divisão de teste. Como você notou, o loops é que o Python ocupa muitos caracteres, geralmente para escrever range, mas também para escrever while _:ou for x in _. Portanto, uma solução Python para golfe deve fazer o possível para usar o menor número possível de loops.

O comentário de feersum "Os programas com o melhor golfe geralmente fazem conexões surpreendentes entre diferentes partes dos programas" é muito aplicável aqui. A verificação primária pode parecer uma sub-rotina separada para a qual all(x%i for i in range(2,x))é a expressão clássica. Mas faremos de outra maneira.

A idéia é usar o Teorema de Wilson . Para cada potencial prime k, mantemos um produto em execução (k-1)!e verificamos se é um múltiplo de k. Podemos acompanhar (k-1)!enquanto testamos o potencial kde serem os principais palíndromos mantendo um produto em execução P.

Na verdade, usaremos a versão mais forte do Teorema de Wilson, que (k-1)! % ké igual a 0 para knúmeros compostos e apenas para números compostos, exceto no caso de k=4doações 2e, portanto, exatamente (k-1)!**2 % kigual 0aos números compostos. Atualizaremos Ppara igual k!**2pela atualização P*=k*k.

(Veja esta resposta para este método usado para encontrar números primos em Python.)

Juntando tudo:

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

Isso ainda não foi totalmente praticado - a condição em particular é escrita de maneira ineficiente. Podemos comprimir a condição para verificar se ké um palíndromo e, ao mesmo tempo, impor outras condições por meio de uma desigualdade encadeada.

def p(n):
 k=P=1
 while`k`*(P%k>0>n-k)!=`k`[::-1]:P*=k*k;k+=1
 return k
xnor
fonte
1
uma solução deslumbrante. sou grato por vê-lo, mesmo que isso vá além do meu alcance ... Até agora, minhas melhorias no golfe eram uma questão de aprender truques como usar backticks em vez de str, mas esses truques só conseguem um muito ... as grandes melhorias vêm de algoritmos melhores, como sempre.
KJo
1
Após a solução da FryAmTheEggman, pode-se reescrever a condição de `k`*(k>n)*(P%k>0)!=`k`[::-1]que raspa 4
KJo
1
@kjo Sim, e ainda mais similarmente, se você combinar as desigualdades em uma única. Você também deve verificar o Python Golf Practice também para ver truques como este
xnor
isso é muito bem feito! obrigado a todos! esse foi um tópico muito revelador para mim ... os menores comprimentos de solução que conheço, para Python 2.7 e 3.3, são 28 e 32 caracteres, respectivamente, menores que a versão que publiquei originalmente (meu melhor esforço absoluto) e quando descobri isso, fiquei incrédulo; Eu pensei que tinha que haver algum jogo sujo em algum lugar (por exemplo, fazendo engenharia reversa do programa de teste de solução automatizado). mas, depois de ver como os especialistas daqui rapidamente cortaram até 16 caracteres do meu melhor esforço, agora estou mais disposto a acreditar nesses números.
Kjo
@kjo Fico feliz que você esteja vendo a mágica da magia do golfe. Você está dizendo que há uma solução de 55 caracteres? Se assim for, estou intrigado. Isso é um problema de anarquia no golfe? Você poderia me vincular à declaração do problema? Pode haver atalhos possíveis nas lacunas nos casos de teste, em particular a exceção com o número 4. #
224
3

AFAIK, na verdade não.

O alcance é comumente usado em golfinhos python porque é a maneira mais curta de gerar listas de números crescentes / decrescentes.

Dito isto, parece ser um pouco (7 bytes) mais curto para evitar o uso do range e, em vez disso, chamar um loop while empacotado:

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

Obrigado a @xnor (como sempre) por melhorar a lógica da condição while :)

FryAmTheEggman
fonte
@kjo Nenhum problema :) Você pode querer adicionar o que você me disse sobre ele ter que ser uma função não recursiva para a questão, caso contrário, esta resposta é bastante pobre;)
FryAmTheEggman
2
Você pode salvar na condição negando implicitamente: while(`n`!=`n`[::-1])+0in(n%i for i in range(2,n)):n+=1. Não consigo me livrar do primeiro par de parênteses devido a problemas de precedência do operador.
Xnor
2
Livrando-se das parênteses:while`n`*all(n%i for i in range(2,n))!=`n`[::-1]:n+=1
xnor
@FryAmTheEggman: é muito esportista da sua parte! feito
KJo
1

Ao usar algoritmos iterativos, como o método Newton ou calcular fractais, em que você geralmente precisa executar iterações sem realmente se importar com o índice, você pode salvar alguns caracteres, iterando sobre cadeias curtas.

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

Em sete iterações, isso se iguala range. Para mais iterações, use backtics e grandes números

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

Isso é executado 56349 vezes, o que deve ser suficiente para todos os fins práticos. Brincar com números e operadores permite codificar uma grande variedade de números dessa maneira.

DenDenDo
fonte
Embora isso seja interessante, você pode ver claramente que ele se importa com o índice (como o candidato principal é o índice). Eu acho que isso se encaixa melhor como uma parte de esta resposta na página de dicas em vez :) (Observe também que '1'*4é menor do que 'asdf')
FryAmTheEggman