Java 7+, n = 50 in ~ 30 seg no TIO
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.Random;
class Main{
public static void main(String[] a){
int n=50;
Random randomGenerator = new Random();
int i = n+1;
int squaredN = n*n;
int[]randomIntegers = new int[i];
randomIntegers[n] = squaredN;
while(true){
for(i=n; i-->1; ){
randomIntegers[i] = randomGenerator.nextInt(squaredN);
}
Set<Integer> result = new HashSet<>();
Arrays.sort(randomIntegers);
for(i=n; i-->0; ){
result.add(randomIntegers[i+1] - randomIntegers[i]);
}
if(!result.contains(0) && result.size()==n){
System.out.println(result);
return;
}
}
}
}
A versão ungolfed da minha resposta para a versão code-golf deste desafio, por enquanto, com apenas uma pequena alteração: java.util.Random#nextInt(limit)
é usada em vez de (int)(Math.random()*limit)
para um número inteiro no intervalo [0, n)
, pois é duas vezes mais rápido .
Experimente online.
Explicação:
Abordagem utilizada:
O código é dividido em duas partes:
- Gere uma lista da
n
quantidade de números inteiros aleatórios que somam n squared
.
- Em seguida, verifica se todos os valores são únicos e nenhum é zero, e se um deles é falsey, ele tentará a etapa 1 novamente, enxaguando e repetindo até obtermos um resultado.
A etapa 1 é realizada com as seguintes subetapas:
1) Gere uma matriz de n-1
quantidade de números inteiros aleatórios no intervalo [0, n squared)
. E adicione 0
e n squared
a esta lista. Isso é feito no O(n+1)
desempenho.
2) Em seguida, ele classificará o array com o builtin java.util.Arrays.sort(int[])
. Isso é feito no O(n*log(n))
desempenho, conforme declarado nos documentos:
Classifica a matriz especificada de ints em ordem numérica crescente. O algoritmo de classificação é um quicksort ajustado, adaptado de Jon L. Bentley e "Engineering a Sort Function" de M. Douglas McIlroy, Software-Practice and Experience, vol. 23 (11) P. 1249-1265 (novembro de 1993). Esse algoritmo oferece desempenho n * log (n) em muitos conjuntos de dados que fazem com que outras classificações rápidas sejam degradadas para desempenho quadrático.
3) Calcule a diferença entre cada par. Essa lista resultante de diferenças conterá n
números inteiros que somam n squared
. Isso é feito no O(n)
desempenho.
Aqui está um exemplo:
// n = 4, nSquared = 16
// n-1 amount of random integers in the range [0, nSquared):
[11, 2, 5]
// Add 0 and nSquared to it, and sort:
[0, 2, 5, 11, 16]
// Calculate differences:
[2, 3, 6, 5]
// The sum of these differences will always be equal to nSquared
sum([2, 3, 6, 5]) = 16
Portanto, essas três etapas acima são muito boas para o desempenho, diferentemente da etapa 2 e do ciclo da coisa toda, que é uma força bruta básica. A etapa 2 é dividida nestas sub-etapas:
1) A lista de diferenças já está salva em a java.util.Set
. Ele verificará se o tamanho deste conjunto é igual a n
. Se for, significa que todos os valores aleatórios que geramos são únicos.
2) E também verificará se não contém nenhum 0
no conjunto, pois o desafio solicita valores aleatórios no intervalo [1, X]
, onde X
é n squared
menos a soma de [1, ..., n-1]
, conforme declarado por @Skidsdev no comentário abaixo.
Se uma das duas opções acima (nem todos os valores forem únicos ou houver zero), ela gerará uma nova matriz e definirá novamente redefinindo a etapa 1. Isso continua até que tenhamos um resultado. Por esse motivo, o tempo pode variar bastante. Eu já vi isso terminar em 3 segundos uma vez no TIO n=50
, mas também em 55 segundos uma vez n=50
.
Prova de uniformidade:
Não tenho muita certeza de como provar isso para ser completamente honesto. O java.util.Random#nextInt
é uniforme, com certeza, conforme descrito nos documentos:
Retorna o próximo valor pseudoaleatório, distribuído uniformemente, a int
partir desta sequência do gerador de números aleatórios. O contrato geral de nextInt
é que um int
valor seja gerado e retornado pseudo-aleatoriamente. Todos os 2 32int
valores possíveis são produzidos com (aproximadamente) probabilidade igual.
As diferenças entre esses valores aleatórios (classificados) não são, obviamente, uniformes, mas os conjuntos como um todo são uniformes. Novamente, não tenho certeza de como provar isso matematicamente, mas aqui está um script que colocará 10,000
conjuntos gerados (para n=10
) em um mapa com um contador , onde a maioria dos conjuntos é única; alguns repetiram duas vezes; e a ocorrência máxima repetida geralmente está no intervalo [4,8]
.
Instruções de instalação:
Como o Java é uma linguagem bastante conhecida, com muitas informações disponíveis sobre como criar e executar o código Java, vou manter isso breve.
Todas as ferramentas usadas no meu código estão disponíveis no Java 7 (talvez até no Java 5 ou 6, mas vamos usar 7 apenas por precaução). Eu tenho certeza que o Java 7 já está arquivado, então eu sugiro fazer o download do Java 8 para executar meu código.
Reflexões sobre melhorias:
Gostaria de encontrar uma melhoria para a verificação de zeros e verificar se todos os valores são únicos. Eu poderia verificar 0
antes, certificando-me de que o valor aleatório que adicionamos à matriz ainda não esteja nela, mas isso significaria algumas coisas: a matriz deve ser uma ArrayList
para que possamos usar o método interno .contains
; um loop while deve ser adicionado até encontrarmos um valor aleatório que ainda não esteja na lista. Como a verificação de zero agora é feita .contains(0)
no conjunto (que é verificado apenas uma vez), é mais provável que seja melhor verificar o desempenho nesse ponto, em comparação com adicionar o loop .contains
na lista, que será verificado pelo menos n
vezes , mas provavelmente mais.
Quanto à verificação de exclusividade, só temos nossa n
quantidade de números inteiros aleatórios que somam n squared
após a etapa 1 do programa; portanto, só assim podemos verificar se todos são únicos ou não. Pode ser possível manter uma lista classificável em vez de matriz e verificar as diferenças entre elas, mas duvido seriamente que melhore o desempenho do que apenas colocá-las em Set
e verifique se o tamanho desse conjunto é n
uma vez.