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
nquantidade 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-1quantidade de números inteiros aleatórios no intervalo [0, n squared). E adicione 0e n squareda 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á nnú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 0no conjunto, pois o desafio solicita valores aleatórios no intervalo [1, X], onde Xé n squaredmenos 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 intpartir desta sequência do gerador de números aleatórios. O contrato geral de nextInté que um intvalor 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,000conjuntos 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 0antes, 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 ArrayListpara 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 .containsna lista, que será verificado pelo menos nvezes , mas provavelmente mais.
Quanto à verificação de exclusividade, só temos nossa nquantidade de números inteiros aleatórios que somam n squaredapó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 Sete verifique se o tamanho desse conjunto é numa vez.