J'ai vu cette question sur Reddit, et aucune solution positive n'a été présentée, et j'ai pensé que ce serait une question parfaite à poser ici. C'était dans un fil sur les questions d'entrevue:
Ecrivez une méthode qui prend un tableau int de taille m et renvoie (True/False) si le tableau est constitué des nombres n ... n + m-1, de tous les nombres compris dans cette plage et de tous les nombres compris dans cette plage. Le tableau n'est pas garanti pour être trié. (Par exemple, {2,3,4} renverrait true. {1,3,1} renverrait false, {1,2,4} renverrait false.
Le problème que j’avais avec celui-ci, c’est que mon intervieweur me demandait sans cesse d’optimiser (plus vite O (n), moins de mémoire, etc.), au point où il prétendait pouvoir le faire en un seul passage du tableau en utilisant Mémoire. Je n'ai jamais compris cela.
En même temps que vos solutions, veuillez indiquer si elles supposent que le tableau contient des éléments uniques. Indiquez également si votre solution suppose que la séquence commence à 1. (J'ai légèrement modifié la question pour permettre les cas où elle passe 2, 3, 4 ...)
edit: Je suis maintenant d’avis qu’il n’existe pas d’algorithme linéaire en temps et en espace constant qui traite les doublons. Quelqu'un peut-il vérifier cela?
Le problème des doublons revient à tester pour voir si le tableau contient des doublons dans O(n) heure, O(1) espace. Si cela peut être fait, vous pouvez simplement tester d'abord et s'il n'y a pas de doublons, exécutez les algorithmes publiés. Alors pouvez-vous tester les dupes dans l'espace O(n) O(1)?
En supposant que les nombres inférieurs à un ne soient pas autorisés et qu'il n'y ait pas de doublons, il existe une identité de sommation simple pour cela - la somme des nombres de 1
à m
par incréments de 1
est (m * (m + 1)) / 2
. Vous pouvez ensuite additionner le tableau et utiliser cette identité.
Vous pouvez savoir s'il y a une dupe sous les garanties ci-dessus, plus la garantie qu'aucun nombre ne soit supérieur à m ou inférieur à n (qui peut être vérifié dans O(N)
)
L'idée en pseudo-code:
0) Commence à N = 0
1) Prenez le Nième élément de la liste.
2) Si ce n'est pas au bon endroit si la liste a été triée, vérifiez où elle devrait être.
3) Si l'endroit où il devrait être déjà a le même numéro, vous avez une dupe - RETURN TRUE
4) Sinon, échangez les nombres (pour placer le premier nombre au bon endroit).
5) Avec le numéro que vous venez d'échanger, est-il au bon endroit?
6) Si non, retournez à l'étape deux.
7) Sinon, commencez à la première étape avec N = N + 1. Si cela se situe au-delà de la fin de la liste, vous n’avez pas de dupes.
Et, oui, cela fonctionne dans O(N)
bien que cela puisse ressembler à O(N ^ 2)
Cette solution fonctionne sous l'hypothèse que vous pouvez modifier le tableau, puis utilise le tri Radix sur place (qui permet d'atteindre la vitesse O(N)
).
D'autres solutions mathématiques ont été avancées, mais je ne suis pas sûr qu'aucune d'entre elles ait été prouvée. Plusieurs sommes peuvent être utiles, mais la plupart d’entre elles se heurtent à une explosion du nombre de bits requis pour représenter la somme, ce qui constitue une violation de la garantie constante d’espace supplémentaire. Je ne sais pas non plus si certains d'entre eux sont capables de produire un nombre distinct pour un ensemble de nombres donné. Je pense qu'une somme de carrés pourrait fonctionner, qui a une formule connue pour le calculer (voir Wolfram's )
Donc, il a été mentionné d'utiliser peut-être somme + somme de carrés. Personne ne savait si cela fonctionnait ou non, et je compris que cela ne devenait un problème que lorsque (x + y) = (n + m), comme le fait 2 + 2 = 1 + 3. Les carrés ont également ce problème grâce à Triples de Pythagore (donc 3 ^ 2 + 4 ^ 2 + 25 ^ 2 == 5 ^ 2 + 7 ^ 2 + 24 ^ 2, et la somme des carrés ne fonctionne pas). Si nous utilisons le dernier théorème de Fermat , nous savons que cela ne peut pas se produire pour n ^ 3. Mais nous ne savons pas non plus s’il n’ya pas de x + y + z = n (à moins que nous ne le sachions et que je ne le sache pas). Donc, rien ne garantit que cela ne casse pas non plus - et si nous continuons dans cette voie, nous manquerons rapidement de bits.
Dans ma joie, cependant, j'ai oublié de noter que vous pouvez casser la somme des carrés, mais que vous créez ainsi une somme normale qui n'est pas valide. Je ne pense pas que vous puissiez faire les deux, mais, comme cela a été noté, nous n’avons aucune preuve de toute façon.
Je dois dire que trouver des contre-exemples est parfois beaucoup plus facile que de prouver des choses! Considérons les séquences suivantes, qui ont toutes une somme de 28 et une somme de carrés de 140:
[1, 2, 3, 4, 5, 6, 7]
[1, 1, 4, 5, 5, 6, 6]
[2, 2, 3, 3, 4, 7, 7]
Je ne pouvais pas trouver de tels exemples de longueur 6 ou moins. Si vous voulez un exemple qui a aussi les valeurs min et max appropriées, essayez celui de longueur 8:
[1, 3, 3, 4, 4, 5, 8, 8]
Un tableau entier de longueur m contient tous les nombres de n à n + m-1 exactement une fois ssf
(Raison: il n'y a que m valeurs dans la plage entière indiquée, donc si le tableau contient m valeurs uniques dans cette plage, il doit les contenir une fois)
Si vous êtes autorisé à modifier le tableau, vous pouvez vérifier les deux en un seul passage dans la liste avec une version modifiée de l'idée d'algorithme de hazzen (il n'est pas nécessaire de faire de sommation):
Je ne suis pas sûr si la modification du tableau d'origine compte dans l'espace supplémentaire maximum autorisé de O (1), mais si ce n'est pas le cas, cela devrait être la solution que l'affiche originale voulait.
En travaillant avec a[i] % a.length
au lieu de a[i]
, vous réduisez le problème à la nécessité de déterminer que vous avez les nombres 0
à a.length - 1
.
Nous prenons cette observation pour acquise et essayons de vérifier si le tableau contient [0, m).
Recherchez le premier noeud qui ne se trouve pas à la bonne position, par exemple.
0 1 2 3 7 5 6 8 4 ; the original dataset (after the renaming we discussed)
^
`---this is position 4 and the 7 shouldn't be here
Échangez ce nombre là où il devrait soit. c'est-à-dire échanger le 7
avec le 8
:
0 1 2 3 8 5 6 7 4 ;
| `--------- 7 is in the right place.
`--------------- this is now the 'current' position
Maintenant, nous répétons cela. En revoyant notre position actuelle, nous demandons:
"Est-ce le bon numéro pour ici?"
En suivant cette règle, nous obtenons:
0 1 2 3 4 5 6 7 8 ; 4 and 8 were just swapped
Cela va progressivement construire la liste correctement de gauche à droite, et chaque numéro sera déplacé au maximum une fois, et donc c'est O (n).
S'il y a des dupes, nous le remarquerons dès qu'il y a une tentative d'échange d'un nombre backwards
dans la liste.
Pourquoi les autres solutions utilisent-elles une somme de chaque valeur? Je pense que cela est risqué, car lorsque vous additionnez ensemble O(n) éléments en un seul numéro, vous utilisez techniquement plus de O(1) espace.
Méthode plus simple:
Étape 1, déterminez s’il ya des doublons. Je ne sais pas si cela est possible dans O(1) espace. Quoi qu'il en soit, retournez false s'il y a des doublons.
Étape 2, parcourez la liste, gardez une trace des éléments les plus bas et les plus élevés .
Étape 3, Est-ce que (plus haut - plus bas) est égal à m? Si oui, retourne vrai.
Tout algorithme en une passe nécessite un stockage Omega (n) bits.
Supposons au contraire qu'il existe un algorithme à une passe qui utilise o(n) bits. Comme il ne fait qu'un seul passage, il doit résumer les n/2 premières valeurs dans l'espace o(n). Puisqu'il existe C (n, n/2) = 2 ^ Theta (n) ensembles possibles de n/2 valeurs tirées de S = {1, ..., n}, il existe deux ensembles distincts A et B de n/2 valeurs telles que l'état de la mémoire est le même après les deux. Si A '= S\A est l'ensemble "correct" pour compléter A, alors l'algorithme ne peut éventuellement pas répondre correctement pour les entrées
A A '- oui
B A '- non
car il ne peut pas distinguer le premier cas du second.
Q.E.D.
boolean determineContinuousArray(int *arr, int len)
{
// Suppose the array is like below:
//int arr[10] = {7,11,14,9,8,100,12,5,13,6};
//int len = sizeof(arr)/sizeof(int);
int n = arr[0];
int *result = new int[len];
for(int i=0; i< len; i++)
result[i] = -1;
for (int i=0; i < len; i++)
{
int cur = arr[i];
int hold ;
if ( arr[i] < n){
n = arr[i];
}
while(true){
if ( cur - n >= len){
cout << "array index out of range: meaning this is not a valid array" << endl;
return false;
}
else if ( result[cur - n] != cur){
hold = result[cur - n];
result[cur - n] = cur;
if (hold == -1) break;
cur = hold;
}else{
cout << "found duplicate number " << cur << endl;
return false;
}
}
}
cout << "this is a valid array" << endl;
for(int j=0 ; j< len; j++)
cout << result[j] << "," ;
cout << endl;
return true;
}
#include<stdio.h>
#define swapxor(a,i,j) a[i]^=a[j];a[j]^=a[i];a[i]^=a[j];
int check_ntom(int a[], int n, int m) {
int i = 0, j = 0;
for(i = 0; i < m; i++) {
if(a[i] < n || a[i] >= n+m) return 0; //invalid entry
j = a[i] - n;
while(j != i) {
if(a[i]==a[j]) return -1; //bucket already occupied. Dupe.
swapxor(a, i, j); //faster bitwise swap
j = a[i] - n;
if(a[i]>=n+m) return 0; //[NEW] invalid entry
}
}
return 200; //OK
}
int main() {
int n=5, m=5;
int a[] = {6, 5, 7, 9, 8};
int r = check_ntom(a, n, m);
printf("%d", r);
return 0;
}
Edit: modification apportée au code pour éliminer les accès illicites à la mémoire.
À mon retour, j'ai entendu parler d'un algorithme de tri très intelligent de la part de quelqu'un qui travaillait pour la compagnie de téléphone. Ils ont dû trier un grand nombre de numéros de téléphone. Après avoir passé en revue différentes stratégies de tri, ils ont finalement trouvé une solution très élégante: ils venaient de créer un tableau de bits et traitaient le décalage dans le tableau de bits comme un numéro de téléphone. Ils ont ensuite balayé leur base de données en un seul passage, en modifiant le bit pour chaque nombre à 1. Ensuite, ils ont balayé le tableau de bits une fois, en crachant les numéros de téléphone des entrées dont le bit était défini sur haut.
Dans le même ordre d'idées, je pense que vous pouvez utiliser les données du tableau comme structure de métadonnées pour rechercher les doublons. Dans le pire des cas, vous pourriez avoir un tableau séparé, mais je suis assez sûr que vous pouvez utiliser le tableau d'entrée si cela ne vous dérange pas d'échanger un peu.
Je vais laisser de côté le paramètre n pour le moment, b/c qui confond simplement les choses - ajouter un décalage d'index est assez facile à faire.
Considérer:
for i = 0 to m
if (a[a[i]]==a[i]) return false; // we have a duplicate
while (a[a[i]] > a[i]) swapArrayIndexes(a[i], i)
sum = sum + a[i]
next
if sum = (n+m-1)*m return true else return false
Ce n'est pas O(n) - probablement plus proche de O (n Log n) - mais cela fournit un espace constant et peut fournir un vecteur d'attaque différent pour le problème.
Si nous voulons O (n), l’utilisation d’un tableau d’octets et de quelques opérations sur les bits fournira le contrôle de duplication avec un n/32 octets supplémentaires de mémoire utilisée (en supposant que les inits sont de 32 bits).
EDIT: L’algorithme ci-dessus pourrait être encore amélioré en ajoutant la vérification de somme à l’intérieur de la boucle et en vérifiant:
if sum > (n+m-1)*m return false
de cette façon, il échouera rapidement.
Votez-moi si je me trompe, mais je pense que nous pouvons déterminer s'il y a des doublons ou si nous n'utilisons pas la variance. Parce que nous connaissons la moyenne à l’avance (n + (m-1)/2 ou quelque chose comme ça), nous pouvons simplement résumer les nombres et le carré de différence pour dire si la somme correspond à l’équation (mn + m(m-1)/2) et la variance est (0 + 1 + 4 + ... + (m-1) ^ 2)/m. Si la variance ne correspond pas, il est probable que nous ayons un doublon.
MODIFIER: la variance est supposée être (0 + 1 + 4 + ... + [(m-1)/2] ^ 2) * 2/m, car la moitié des éléments est inférieure à la moyenne et l'autre moitié est supérieure à la moyenne.
S'il y a un doublon, un terme de l'équation ci-dessus sera différent de la séquence correcte, même si un autre doublon annule complètement le changement de moyenne. Donc, la fonction ne retourne vrai que si somme et variance correspondent aux valeurs désirées, que nous pouvons calculer à l’avance.
En supposant que vous ne connaissiez que la longueur du tableau et que vous êtes autorisé à modifier le tableau, vous pouvez le faire dans l'espace O(1) et le temps O(n).
Le processus comporte deux étapes simples . 1. "modulo sort" le tableau. [5,3,2,4] => [4,5,2,3] (O (2n)) 2. Vérifiez que le voisin de chaque valeur est supérieur à lui-même (modulo) (O (n))
En tout, il vous faut au maximum 3 passages dans le tableau.
Le type modulo est la partie «difficile», mais l'objectif est simple. Prenez chaque valeur du tableau et stockez-la à sa propre adresse (longueur modulo). Cela nécessite un passage dans le tableau, en passant en boucle sur chaque emplacement en "évacuant" sa valeur en le permutant vers son emplacement correct et en déplaçant la valeur à sa destination. Si vous vous déplacez dans une valeur qui correspond à la valeur que vous venez d'expulser, vous avez un doublon et vous pouvez quitter plus tôt . Dans le pire des cas, il s'agit de O (2n).
La vérification consiste en un seul passage dans le tableau, examinant chaque valeur avec son voisin le plus proche. Toujours O (n).
L'algorithme combiné est O (n) + O (2n) = O(3n) = O (n)
Pseudocode de ma solution:
foreach (valeurs []) while (valeurs [i] non congruentes avec i) à expulser = valeurs [i] expulser (valeurs [i]) // basculer vers l'emplacement "approprié" if (valeurs [i]% longueur == à-être-expulsé% longueur) retourne faux; // un "duplicata" est arrivé lorsque nous avons expulsé ce nombre fin tout en .__ fin à foreach foreach (valeurs []) if ((valeurs [i] +1)% longueur! = valeurs [i + 1]% longueur) renvoie false end foreach
J'ai inclus la preuve de concept de code Java ci-dessous, ce n'est pas joli, mais il passe tous les tests unitaires que j'ai faits pour cela. J'appelle cela un 'StraightArray', car ils correspondent à la main de poker d'une suite (une séquence contiguë ne donnant pas le sens).
public class StraightArray {
static int evict(int[] a, int i) {
int t = a[i];
a[i] = a[t%a.length];
a[t%a.length] = t;
return t;
}
static boolean isStraight(int[] values) {
for(int i = 0; i < values.length; i++) {
while(values[i]%values.length != i) {
int evicted = evict(values, i);
if(evicted%values.length == values[i]%values.length) {
return false;
}
}
}
for(int i = 0; i < values.length-1; i++) {
int n = (values[i]%values.length)+1;
int m = values[(i+1)]%values.length;
if(n != m) {
return false;
}
}
return true;
}
}
Ceci utilise le pseudocode suggéré par Hazzen et certaines de mes propres idées. Cela fonctionne aussi pour les nombres négatifs et ne nécessite aucune somme de la somme des carrés.
function testArray($nums, $n, $m) {
// check the sum. PHP offers this array_sum() method, but it's
// trivial to write your own. O(n) here.
if (array_sum($nums) != ($m * ($m + 2 * $n - 1) / 2)) {
return false; // checksum failed.
}
for ($i = 0; $i < $m; ++$i) {
// check if the number is in the proper range
if ($nums[$i] < $n || $nums[$i] >= $n + $m) {
return false; // value out of range.
}
while (($shouldBe = $nums[$i] - $n) != $i) {
if ($nums[$shouldBe] == $nums[$i]) {
return false; // duplicate
}
$temp = $nums[$i];
$nums[$i] = $nums[$shouldBe];
$nums[$shouldBe] = $temp;
}
}
return true; // huzzah!
}
var_dump(testArray(array(1, 2, 3, 4, 5), 1, 5)); // true
var_dump(testArray(array(5, 4, 3, 2, 1), 1, 5)); // true
var_dump(testArray(array(6, 4, 3, 2, 0), 1, 5)); // false - out of range
var_dump(testArray(array(5, 5, 3, 2, 1), 1, 5)); // false - checksum fail
var_dump(testArray(array(5, 4, 3, 2, 5), 1, 5)); // false - dupe
var_dump(testArray(array(-2, -1, 0, 1, 2), -2, 5)); // true
(ne peut pas poster comme un commentaire)
@popopome
Pour a = {0, 2, 7, 5,}
, il retourne true
(signifie que a
est une permutation de la plage [0, 4)
), mais il doit renvoyer false
dans ce cas (a
n'est évidemment pas une permutaton de [0, 4)
).
Un autre exemple de compteur: {0, 0, 1, 3, 5, 6, 6}
- toutes les valeurs sont dans la plage mais il y a des doublons.
Je pourrais implémenter de manière incorrecte l'idée (ou les tests) du popopome, donc voici le code:
bool isperm_popopome(int m; int a[m], int m, int n)
{
/** O(m) in time (single pass), O(1) in space,
no restrictions on n,
no overflow,
a[] may be readonly
*/
int even_xor = 0;
int odd_xor = 0;
for (int i = 0; i < m; ++i)
{
if (a[i] % 2 == 0) // is even
even_xor ^= a[i];
else
odd_xor ^= a[i];
const int b = i + n;
if (b % 2 == 0) // is even
even_xor ^= b;
else
odd_xor ^= b;
}
return (even_xor == 0) && (odd_xor == 0);
}
Donc, il existe un algorithme qui prend O (n ^ 2) qui ne nécessite pas de modifier le tableau en entrée et qui prend un espace constant.
Premièrement, supposons que vous connaissiez n
et m
. Il s’agit d’une opération linéaire, elle n’ajoute donc pas de complexité supplémentaire. Ensuite, supposons qu'il existe un élément égal à n
et un élément égal à n+m-1
et que tous les autres éléments sont en [n, n+m)
. Compte tenu de cela, nous pouvons réduire le problème à un tableau avec des éléments dans [0, m)
.
Maintenant, puisque nous savons que les éléments sont liés par la taille du tableau, nous pouvons traiter chaque élément comme un nœud avec un seul lien vers un autre élément; en d'autres termes, le tableau décrit un graphe dirigé. Dans ce graphe orienté, s'il n'y a pas d'éléments dupliqués, chaque nœud appartient à un cycle, c'est-à-dire qu'un nœud est accessible de lui-même en m
ou moins étapes. S'il y a un élément en double, il existe alors un nœud qui n'est pas accessible du tout.
Donc, pour détecter ceci, vous parcourez tout le tableau du début à la fin et déterminez si chaque élément revient à lui-même par étapes <=m
. Si un élément n'est pas accessible dans les étapes <=m
, vous avez un doublon et pouvez renvoyer false. Sinon, lorsque vous aurez fini de visiter tous les éléments, vous pourrez retourner vrai:
for (int start_index= 0; start_index<m; ++start_index)
{
int steps= 1;
int current_element_index= arr[start_index];
while (steps<m+1 && current_element_index!=start_index)
{
current_element_index= arr[current_element_index];
++steps;
}
if (steps>m)
{
return false;
}
}
return true;
Vous pouvez optimiser cela en stockant des informations supplémentaires:
sum_of_steps
.m-sum_of_steps
. Si vous ne revenez pas à l'élément de départ et que vous ne visitez pas un élément avant l'élément de départ, vous avez trouvé une boucle contenant des éléments en double et pouvez renvoyer false
.C'est toujours O (n ^ 2), par ex. {1, 2, 3, 0, 5, 6, 7, 4}
, mais c'est un peu plus rapide.
note : ce commentaire est basé sur le texte original de la question (corrigé depuis)
Si la question est posée exactement comme écrit ci-dessus (et ce n'est pas juste une faute de frappe) et pour un tableau de taille n, la fonction doit retourner (True/False) si le tableau est constitué des nombres 1 ... n + 1,
... alors la réponse sera toujours fausse car le tableau avec tous les nombres 1 ... n + 1 sera de taille n + 1 et non pas n. on peut donc répondre à la question en O (1). :)
Tout tableau contigu [n, n + 1, ..., n + m-1] peut être mappé sur un intervalle de "base" [0, 1, ..., m] à l'aide de l'opérateur modulo. Pour chaque i dans l'intervalle, il y a exactement un i% m dans l'intervalle de base et inversement.
Tout tableau contigu a également un «span» m (maximum - minimum + 1) égal à sa taille.
En utilisant ces faits, vous pouvez créer un tableau booléen "rencontré" de même taille contenant initialement tous les faux, puis, lors de la visite du tableau en entrée, définissez leurs éléments "rencontrés" sur true.
Cet algorithme est O(n) dans l'espace, O(n) dans le temps et vérifie les doublons.
def contiguous( values )
#initialization
encountered = Array.new( values.size, false )
min, max = nil, nil
visited = 0
values.each do |v|
index = v % encountered.size
if( encountered[ index ] )
return "duplicates";
end
encountered[ index ] = true
min = v if min == nil or v < min
max = v if max == nil or v > max
visited += 1
end
if ( max - min + 1 != values.size ) or visited != values.size
return "hole"
else
return "contiguous"
end
end
tests = [
[ false, [ 2,4,5,6 ] ],
[ false, [ 10,11,13,14 ] ] ,
[ true , [ 20,21,22,23 ] ] ,
[ true , [ 19,20,21,22,23 ] ] ,
[ true , [ 20,21,22,23,24 ] ] ,
[ false, [ 20,21,22,23,24+5 ] ] ,
[ false, [ 2,2,3,4,5 ] ]
]
tests.each do |t|
result = contiguous( t[1] )
if( t[0] != ( result == "contiguous" ) )
puts "Failed Test : " + t[1].to_s + " returned " + result
end
end
Un tableau contient N nombres et vous voulez déterminer si deux des nombres Totalisent un nombre K. Par exemple, si l'entrée est 8,4, 1,6 et K est égal à 10, est oui (4 et 6). Un numéro peut être utilisé deux fois. Faites ce qui suit . A. Donnez un algorithme O(N2) pour résoudre ce problème ..__ b. Donnez un algorithme O (N log N) pour résoudre ce problème. (Conseil: Triez d'abord les éléments . Après cela, vous pourrez résoudre le problème en temps linéaire.) C. Codez les deux solutions et comparez les temps d'exécution de vos algorithmes . 4.
Si vous voulez connaître la somme des nombres [n ... n + m - 1]
, utilisez simplement cette équation.
var sum = m * (m + 2 * n - 1) / 2;
Cela fonctionne pour tout nombre, positif ou négatif, même si n est un nombre décimal.
(pour éviter une mauvaise interprétation du pseudo-code)
Exemple de compteur: {1, 1, 2, 4, 6, 7, 7}
.
int pow_minus_one(int power)
{
return (power % 2 == 0) ? 1 : -1;
}
int ceil_half(int n)
{
return n / 2 + (n % 2);
}
bool isperm_b3_3(int m; int a[m], int m, int n)
{
/**
O(m) in time (single pass), O(1) in space,
doesn't use n
possible overflow in sum
a[] may be readonly
*/
int altsum = 0;
int mina = INT_MAX;
int maxa = INT_MIN;
for (int i = 0; i < m; ++i)
{
const int v = a[i] - n + 1; // [n, n+m-1] -> [1, m] to deal with n=0
if (mina > v)
mina = v;
if (maxa < v)
maxa = v;
altsum += pow_minus_one(v) * v;
}
return ((maxa-mina == m-1)
and ((pow_minus_one(mina + m-1) * ceil_half(mina + m-1)
- pow_minus_one(mina-1) * ceil_half(mina-1)) == altsum));
}
En Python:
def ispermutation(iterable, m, n):
"""Whether iterable and the range [n, n+m) have the same elements.
pre-condition: there are no duplicates in the iterable
"""
for i, elem in enumerate(iterable):
if not n <= elem < n+m:
return False
return i == m-1
print(ispermutation([1, 42], 2, 1) == False)
print(ispermutation(range(10), 10, 0) == True)
print(ispermutation((2, 1, 3), 3, 1) == True)
print(ispermutation((2, 1, 3), 3, 0) == False)
print(ispermutation((2, 1, 3), 4, 1) == False)
print(ispermutation((2, 1, 3), 2, 1) == False)
C'est O(m) dans le temps et O(1) dans l'espace. Il ne prend pas en compte les doublons.
Solution alternative:
def ispermutation(iterable, m, n):
"""Same as above.
pre-condition: assert(len(list(iterable)) == m)
"""
return all(n <= elem < n+m for elem in iterable)
ciphwn a raison. Tout cela a à voir avec les statistiques. La question qui se pose est, en termes statistiques, de savoir si la suite de nombres forme une distribution uniforme discrète. Une distribution uniforme discrète est où toutes les valeurs d'un ensemble fini de valeurs possibles sont également probables. Heureusement, il existe des formules utiles pour déterminer si un ensemble discret est uniforme. Premièrement, pour déterminer la moyenne de l'ensemble (a..b) est (a + b)/2 et la variance est de (n.n-1)/12. Ensuite, déterminez la variance de l'ensemble donné:
variance = sum [i=1..n] (f(i)-mean).(f(i)-mean)/n
et ensuite comparer avec la variance attendue. Cela nécessitera deux passages sur les données, une fois pour déterminer la moyenne et une autre fois pour calculer la variance.
Références:
Oops! Je me suis retrouvé pris dans une double question et je n’ai pas vu les solutions déjà identiques ici. Et je pensais avoir enfin fait quelque chose d'original! Voici une archive historique de l'époque où j'étais un peu plus satisfait:
Eh bien, je n'ai aucune certitude si cet algorithme satisfait toutes les conditions. En fait, je n'ai même pas confirmé que cela fonctionnait au-delà de quelques cas de test que j'ai essayés. Même si mon algorithme a des problèmes, j'espère que mon approche suscitera des solutions.
À ma connaissance, cet algorithme fonctionne en mémoire constante et analyse le tableau trois fois. Peut-être un avantage supplémentaire est-il que cela fonctionne pour toute la gamme des nombres entiers, si cela ne faisait pas partie du problème initial.
Je ne suis pas vraiment un pseudo-code, et je pense vraiment que le code pourrait tout simplement avoir plus de sens que les mots. Voici une implémentation que j'ai écrite en PHP. Prenez en compte les commentaires.
function is_permutation($ints) {
/* Gather some meta-data. These scans can
be done simultaneously */
$lowest = min($ints);
$length = count($ints);
$max_index = $length - 1;
$sort_run_count = 0;
/* I do not have any proof that running this sort twice
will always completely sort the array (of course only
intentionally happening if the array is a permutation) */
while ($sort_run_count < 2) {
for ($i = 0; $i < $length; ++$i) {
$dest_index = $ints[$i] - $lowest;
if ($i == $dest_index) {
continue;
}
if ($dest_index > $max_index) {
return false;
}
if ($ints[$i] == $ints[$dest_index]) {
return false;
}
$temp = $ints[$dest_index];
$ints[$dest_index] = $ints[$i];
$ints[$i] = $temp;
}
++$sort_run_count;
}
return true;
}
(pour faciliter les tests)
Contre-exemple (pour la version C): {8, 33, 27, 30, 9, 2, 35, 7, 26, 32, 2, 23, 0, 13, 1, 6, 31, 3, 28, 4, 5, 18, 12, 2, 9, 14, 17, 21, 19, 22, 15, 20, 24, 11, 10, 16, 25}. Ici n = 0, m = 35. Cette séquence manque 34
et a deux 2
.
C'est un O(m) dans le temps et O(1) dans une solution spatiale.
Les valeurs hors plage sont facilement détectées dans O(n) dans le temps et dans O(1) dans l'espace; les tests sont donc concentrés dans la plage (signifie que toutes les valeurs sont dans la plage valide [n, n+m)
) des séquences. Sinon, {1, 34}
est un exemple de compteur (pour la version C, sizeof (int) == 4, représentation binaire standard des nombres).
La principale différence entre la version C et Ruby: L'opérateur <<
fera pivoter les valeurs en C en raison d'une taille finie de (int), mais en Ruby, le nombre augmentera pour tenir compte du résultat, par exemple,
Ruby: 1 << 100 # -> 1267650600228229401496703205376
C: int n = 100; 1 << n // -> 16
En Ruby: check_index ^= 1 << i;
est équivalent à check_index.setbit(i)
. Le même effet pourrait être implémenté en C++: vector<bool> v(m); v[i] = true;
bool isperm_fredric(int m; int a[m], int m, int n)
{
/**
O(m) in time (single pass), O(1) in space,
no restriction on n,
?overflow?
a[] may be readonly
*/
int check_index = 0;
int check_value = 0;
int min = a[0];
for (int i = 0; i < m; ++i) {
check_index ^= 1 << i;
check_value ^= 1 << (a[i] - n); //
if (a[i] < min)
min = a[i];
}
check_index <<= min - n; // min and n may differ e.g.,
// {1, 1}: min=1, but n may be 0.
return check_index == check_value;
}
Les valeurs de la fonction ci-dessus ont été testées par rapport au code suivant:
bool *seen_isperm_trusted = NULL;
bool isperm_trusted(int m; int a[m], int m, int n)
{
/** O(m) in time, O(m) in space */
for (int i = 0; i < m; ++i) // could be memset(s_i_t, 0, m*sizeof(*s_i_t));
seen_isperm_trusted[i] = false;
for (int i = 0; i < m; ++i) {
if (a[i] < n or a[i] >= n + m)
return false; // out of range
if (seen_isperm_trusted[a[i]-n])
return false; // duplicates
else
seen_isperm_trusted[a[i]-n] = true;
}
return true; // a[] is a permutation of the range: [n, n+m)
}
Les tableaux d'entrée sont générés avec:
void backtrack(int m; int a[m], int m, int nitems)
{
/** generate all permutations with repetition for the range [0, m) */
if (nitems == m) {
(void)test_array(a, nitems, 0); // {0, 0}, {0, 1}, {1, 0}, {1, 1}
}
else for (int i = 0; i < m; ++i) {
a[nitems] = i;
backtrack(a, m, nitems + 1);
}
}
ainsi, en un seul passage, vous pouvez calculer le produit des m nombres et calculer m! et voyez si le produit modulo m! est zéro à la fin de la passe
Il se peut que je manque quelque chose, mais c’est ce qui me vient à l’esprit ...
quelque chose comme ça en python
my_list1 = [9,5,8,7,6]
my_list2 = [3,5,4,7]
def consécutif (my_list):
count = 0
prod = fact = 1
for num in my_list:
prod *= num
count +=1
fact *= count
if not prod % fact:
return 1
else:
return 0
imprimer consécutivement (my_list1)
imprimer consécutivement (my_list2)
HotPotato ~ $ python m_consecutive.py
1
0
Je propose ce qui suit:
Choisissez un ensemble fini de nombres premiers P_1, P_2, ..., P_K, et calculez les occurrences des éléments de la séquence d'entrée (moins le minimum) modulo chaque P_i. Le modèle d'une séquence valide est connu.
Par exemple pour une séquence de 17 éléments, modulo 2, il faut avoir le profil: [9 8], modulo 3: [6 6 5], modulo 5: [4 4 3 3 3], etc.
En combinant le test en utilisant plusieurs bases, nous obtenons un test probabiliste de plus en plus précis. Comme les entrées sont limitées par la taille de l’entier, il existe une base finie fournissant un test exact. Ceci est similaire aux tests de pseudo primalité probabilistes.
S_i is an int array of size P_i, initially filled with 0, i=1..K
M is the length of the input sequence
Mn = INT_MAX
Mx = INT_MIN
for x in the input sequence:
for i in 1..K: S_i[x % P_i]++ // count occurrences mod Pi
Mn = min(Mn,x) // update min
Mx = max(Mx,x) // and max
if Mx-Mn != M-1: return False // Check bounds
for i in 1..K:
// Check profile mod P_i
Q = M / P_i
R = M % P_i
Check S_i[(Mn+j) % P_i] is Q+1 for j=0..R-1 and Q for j=R..P_i-1
if this test fails, return False
return True
Compte tenu de cela -
Ecrivez une méthode qui prend un tableau int de taille m ...
Je suppose qu’il est juste de conclure qu’il existe une limite supérieure pour m, égale à la valeur du plus grand int (2 ^ 32 étant typique). En d'autres termes, même si m n'est pas spécifié en tant qu'int, le fait que le tableau ne puisse pas avoir de doublons implique qu'il ne peut pas y avoir plus que le nombre de valeurs que vous pouvez former sur 32 bits, ce qui implique m est limité à être un int aussi.
Si une telle conclusion est acceptable, je propose d'utiliser un espace fixe de (2 ^ 33 + 2) * 4 octets = 34 359 738 376 octets = 34,4 Go pour traiter tous les cas possibles. (Sans compter l'espace requis par le tableau d'entrée et sa boucle).
Bien sûr, pour l'optimisation, je prendrais d'abord m en compte et n'allouerais que le montant réel nécessaire, (2m + 2) * 4 octets.
Si cela est acceptable pour la O(1) contrainte d'espace - pour le problème énoncé -, laissez-moi alors passer à une proposition algorithmique ... :)
Hypothèses : tableau de masses, positif ou négatif, ne dépassant pas 4 octets. Les doublons sont traités. La première valeur peut être n'importe quel int valide. Restreindre m comme ci-dessus.
First, crée un tableau int de longueur 2m-1, aire , et fournit trois variables int: left , diff et right . Remarquez que cela fait 2m + 2 ...
Second, prend la première valeur du tableau en entrée et la copie à la position m-1 dans le nouveau tableau. Initialiser les trois variables.
Third, parcourez les valeurs restantes du tableau en entrée et procédez comme suit pour chaque itération:
J'ai décidé de mettre cela dans le code, et cela a fonctionné.
Voici un exemple de travail utilisant C #:
public class Program
{
static bool puzzle(int[] inAry)
{
var m = inAry.Count();
var outAry = new int?[2 * m - 1];
int diff = 0;
int left = 0;
int right = 0;
outAry[m - 1] = inAry[0];
for (var i = 1; i < m; i += 1)
{
diff = inAry[i] - inAry[0];
if (diff > m - 1 + right || diff < 1 - m + left) return false;
if (outAry[m - 1 + diff] != null) return false;
outAry[m - 1 + diff] = inAry[i];
if (diff > left) left = diff;
if (diff < right) right = diff;
}
return true;
}
static void Main(string[] args)
{
var inAry = new int[3]{ 2, 3, 4 };
Console.WriteLine(puzzle(inAry));
inAry = new int[13] { -3, 5, -1, -2, 9, 8, 2, 3, 0, 6, 4, 7, 1 };
Console.WriteLine(puzzle(inAry));
inAry = new int[3] { 21, 31, 41 };
Console.WriteLine(puzzle(inAry));
Console.ReadLine();
}
}
J'aime l'idée de Greg Hewgill sur le tri Radix. Pour trouver des doublons, vous pouvez trier dans le temps O(N) en fonction des contraintes sur les valeurs de ce tableau.
Pour un temps O(1) sur place O(N) qui restaure l'ordre initial de la liste, vous n'avez pas à effectuer un échange réel sur ce nombre; vous pouvez simplement le marquer avec un drapeau:
//Java: assumes all numbers in arr > 1
boolean checkArrayConsecutiveRange(int[] arr) {
// find min/max
int min = arr[0]; int max = arr[0]
for (int i=1; i<arr.length; i++) {
min = (arr[i] < min ? arr[i] : min);
max = (arr[i] > max ? arr[i] : max);
}
if (max-min != arr.length) return false;
// flag and check
boolean ret = true;
for (int i=0; i<arr.length; i++) {
int targetI = Math.abs(arr[i])-min;
if (arr[targetI] < 0) {
ret = false;
break;
}
arr[targetI] = -arr[targetI];
}
for (int i=0; i<arr.length; i++) {
arr[i] = Math.abs(arr[i]);
}
return ret;
}
Stocker les drapeaux à l'intérieur du tableau donné est une sorte de triche et ne joue pas bien avec la parallélisation. J'essaie toujours de trouver un moyen de le faire sans toucher le tableau dans le temps O(N) et l'espace O (journal N). Vérifier contre la somme et contre la somme des moindres carrés (arr [i] - arr.length/2.0) ^ 2 donne l’impression que cela pourrait fonctionner. La caractéristique que nous connaissons d'un tableau 0 ... m sans doublons est qu'il est uniformément distribué. nous devrions juste vérifier cela.
Maintenant, si seulement je pouvais le prouver.
Je voudrais noter que la solution ci-dessus impliquant factorielle prend O(N) de l'espace pour stocker la factorielle elle-même. N! > 2 ^ N, ce qui prend N octets à stocker.
def test(a, n, m):
seen = [False] * m
for x in a:
if x < n or x >= n+m:
return False
if seen[x-n]:
return False
seen[x-n] = True
return False not in seen
print test([2, 3, 1], 1, 3)
print test([1, 3, 1], 1, 3)
print test([1, 2, 4], 1, 3)
Notez que cela ne fait qu’un passage dans le premier tableau, sans tenir compte de la recherche linéaire impliquée dans not in
. :)
J'aurais aussi pu utiliser une set
en python, mais j'ai opté pour la solution simple, dans laquelle les caractéristiques de performance de set
ne doivent pas être prises en compte.
Mise à jour: Smashery a indiqué que j'avais mal inscrit "quantité de mémoire constante" et que cette solution ne résolvait pas le problème.
Voici une solution en O(N) et en O(1) un espace supplémentaire pour la recherche de doublons: -
public static boolean check_range(int arr[],int n,int m) {
for(int i=0;i<m;i++) {
arr[i] = arr[i] - n;
if(arr[i]>=m)
return(false);
}
System.out.println("In range");
int j=0;
while(j<m) {
System.out.println(j);
if(arr[j]<m) {
if(arr[arr[j]]<m) {
int t = arr[arr[j]];
arr[arr[j]] = arr[j] + m;
arr[j] = t;
if(j==arr[j]) {
arr[j] = arr[j] + m;
j++;
}
}
else return(false);
}
else j++;
}
Explication: -
- Amenez le nombre à la plage (0, m-1) avec arr [i] = arr [i] - n si la valeur renvoyée est fausse.
- pour chaque fois que je vérifie si arr [arr [i]] est inoccupé, sa valeur est inférieure à m
- si c'est le cas swap (arr [i], arr [arr [i]]) et arr [arr [i]] = arr [arr [i]] + m pour signaler qu'il est occupé
- si arr [j] = j et simplement ajouter m et incrémenter j
- si arr [arr [j]]> = m signifie qu'il est occupé, donc la valeur actuelle est dupliquée, donc renvoie false.
- si arr [j]> = m alors sautez
MON MEILLEUR OPTION ACTUELLE
def uniqueSet( array )
check_index = 0;
check_value = 0;
min = array[0];
array.each_with_index{ |value,index|
check_index = check_index ^ ( 1 << index );
check_value = check_value ^ ( 1 << value );
min = value if value < min
}
check_index = check_index << min;
return check_index == check_value;
end
O (n) et espace O(1)
J'ai écrit un script pour des combinaisons de force brute qui pourraient échouer et il n'en a trouvé aucune. Si vous avez un tableau qui contrevient à cette fonction, dites-le. :)
@ J.F. Sebastian
Ce n'est pas un véritable algorithme de hachage. Techniquement, c'est un tableau booléen très efficace et compacté de valeurs "vues".
ci = 0, cv = 0
[5,4,3]{
i = 0
v = 5
1 << 0 == 000001
1 << 5 == 100000
0 ^ 000001 = 000001
0 ^ 100000 = 100000
i = 1
v = 4
1 << 1 == 000010
1 << 4 == 010000
000001 ^ 000010 = 000011
100000 ^ 010000 = 110000
i = 2
v = 3
1 << 2 == 000100
1 << 3 == 001000
000011 ^ 000100 = 000111
110000 ^ 001000 = 111000
}
min = 3
000111 << 3 == 111000
111000 === 111000
Le but de ceci étant principalement que pour "simuler" la plupart des cas problématiques, on utilise des doublons pour le faire. Dans ce système, XOR vous pénalise pour avoir utilisé la même valeur deux fois et suppose que vous l'avez déjà fait 0 fois.
Les mises en garde étant bien entendu:
$x
dans ( 1 << $x > 0 )
l’efficacité ultime dépend de la manière dont votre système sous-jacent met en œuvre les capacités suivantes:
edit Noté, les déclarations ci-dessus semblent déroutantes. En supposant une machine parfaite, où un "entier" est un registre avec une précision infinie, qui peut toujours effectuer un ^ b dans le temps O(1).
Mais à défaut de ces hypothèses, il faut commencer à se poser la question de la complexité algorithmique des mathématiques simples.