Collection d'objet

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : Collection d'objet

par Hubert Roksor » 18 juin 2007, 10:28

Ben c'est une sélection aléatoire sur un tableau, et comme ta "collection d'objet" n'est qu'un wrapper autour d'un tableau le principe reste le même (voir la méthode que j'ai posté plus haut).

par Wells83 » 18 juin 2007, 09:40

Dans le cadre de ma question il aurait été intéressant de prolonger le test en m'étant en scène cette fois ci un tirage aléatoire dans une collection d'objets.

par naholyr » 15 juin 2007, 14:04

Par curiosité (c'est à peine HS, disons un peu en retard ;)) j'ai testé les différentes manières de récupérer un éléments de tableau au hasard :
// Entrée : tableau "simple" (non associatif)
// Sortie : un élément au hasard
function my_array_rand_value($tableau) {
	return $tableau[mt_rand(1,count($tableau)-1)];
}

// Entrée : tableau quelconque
// Sortie : un élément au hasard
function my_array_rand_value_assoc($tableau) {
	$values = array_values($tableau);
	return $values[mt_rand(0,count($tableau)-1)];
}

// Entrée : tableau quelconque
// Sortie : un élément au hasard
function array_rand_value($tableau) {
	return $tableau[array_rand($tableau)];
}

// Construction d'un tableau
$tableau = array();
$tableau_assoc = array();
for ($i=0; $i<$nbElements; $i++) {
	$tableau[] = uniqid();
	$tableau_assoc[uniqid()] = uniqid();
}
$bench->setMarker('Construction des tableaux de '.$nbElements.' éléments');

// Tests des 4 possibilités
for ($i=0; $i<$nbIterations; ++$i) $res = my_array_rand_value($tableau);
$bench->setMarker($nbIterations.' appels à my_array_rand_value($tableau)');
for ($i=0; $i<$nbIterations; ++$i) $res = my_array_rand_value_assoc($tableau_assoc);
$bench->setMarker($nbIterations.' appels à my_array_rand_value_assoc($tableau_assoc)');
for ($i=0; $i<$nbIterations; ++$i) $res = array_rand_value($tableau);
$bench->setMarker($nbIterations.' appels à array_rand_value($tableau)');
for ($i=0; $i<$nbIterations; ++$i) $res = array_rand_value($tableau_assoc);
$bench->setMarker($nbIterations.' appels à array_rand_value($tableau_assoc)');
Voici les résultats pour un tout petit tableau (5 éléments, 100'000 itérations) :
Image

Pour un tableau moyen (500 éléments, 100'000 itérations) :
Image

Pour un grand tableau (50'000 éléments, 1'000 itérations) :
Image

Si on compare avec les valeurs par itération en fonction du nombre d'éléments, c'est édifiant :
Image
Bien sûr il faut lire ça en ayant en tête que A et C sont sur des tableaux "simple" alors que B et D sont sur des tableaux associatifs, donc il aurait fallu comparer A/C et B/D, mais les résultats auraient été très proches étant donné que C/D est toujours très proche de 1.
Mais ce qui apparaît le plus flagrant, et qui est la donnée vraiment intéressante c'est que $tableau[mt_rand(0,count($tableau)-1)] se calcule en un temps totalement indépendant du nombre d'éléments du tableau, et ça c'est très fort ;) J'ai fait le test avec un tableau de 6'900'000 éléments (je ne peux pas plus avec ma RAM) et ça donne le même temps de 2.9 us.

En conclusion :
- Sur un tableau associatif il vaut TOUJOURS mieux utiliser array_rand(), le rapport passe vite à du 1 pour 10.
- Sur un tableau "simple" l'utilisation de mt_rand()+count() devient plus rapide à partir de 15-20 éléments, et avant elle est vraiment très proche. Donc il vaut quasiment toujours utiliser mt_rand+count ;) le rapport passe vite à du 1 pour 5.
- Si on peut utiliser un tableau simple au lieu d'un associatif, il vaut mieux utiliser le simple.

par Wells83 » 15 juin 2007, 10:01

Oui c'est la solution que j'utilise actuellement.

Seulement le problème est que par exemple mon vaisseau peut avoir comme ordre de tirer uniquement sur les petits vaisseau ou inversement sur les gros.

Il me faut alors tirer une cible au hasard dans les vaisseaux vivants d'une certaine taille.

Puis après on peut demande qu'un vaisseau tire que sur un gros vaisseau qui a déja été endommager. ect......

dans ce cas la il faut passer par un filtrage non?

par Genova » 13 juin 2007, 13:22

Dans ce cas tu peux créer un tableau avec des références sur tous tes objets qui sont encore vivants, et supprimés les entrées du tableau en cas de "mort". Ton array_rand() ne piochera ainsi que chez les vivants.

par Wells83 » 13 juin 2007, 12:14

Ben le problème c'est qu'au début du combat toutes les cibles sont vivantes, donc tu mettra peu de temps a tirer un valeur correcte.

Inversement plus le combat avance plus il y a de morts et plus le temps pour trouver une cible va exploser.

par Hubert Roksor » 12 juin 2007, 18:46

titerm va pas être content mais on peut prédire sans trop se mouiller que les performances d'une boucle d'array_rand() sur un array_filter() avec un callback perso sur un ArrayObject casté en (array) vont être : pas bonnes.

D'ailleurs, où en es-tu au niveau du code ? Comment gères-tu ta "collection d'objets" ? Au niveau des perfs, ça donne quoi ?

Avec plus d'infos sur le code en question il devrait être possible de deviser des alternatives. Par exemple, le code qui filtre/trie/tire un vaisseau au hasard pourrait appartenir à la classe qui héberge tous ces objets. Plutôt que de caster un objet en array et d'utiliser array_rand() tu peux peut-être exploiter certaines propriétés de tes données. Par exemple, si tes objets sont dans un tableau indexé numériquement tu n'as pas besoin d'array_rand() pour tirer une clé au hasard puisque tu sais déjà quelles sont les clés du tableau (de 0 à n, où n est le nombre d'objets moins 1).
public function un_au_hasard()
{
    return $this->obj[mt_rand(0, count($this->obj) - 1)];
}
Dans certains cas, plutôt que d'appliquer ton filtre à tous les objets de ta collection puis en tirer un au hasard, il est plus performant de faire le contraire : en tirer un au hasard, voir si c'est une cible valide et recommencer si nécessaire. C'est une approche dite "optimiste". Ça ne marche que si tu as de bonnes probabilités de tomber sur le bon dans les premiers coups, et que les calculs pour en tirer un au hasard n'excèdent pas les calculs pour filtrer toute la collection.

par Wells83 » 12 juin 2007, 16:24

Ok je vois et coté perf ca donne quoi ca? Sachant que je peux filtrer X fois sur X paramètres, le tout dynamiquement (les paramètres changent)

par Klomac » 08 juin 2007, 18:53

Tu fais appel à la fonction array_filter() qui te permet elle-même d'appeler une fonction de callback (un filtre si tu préfères) sur ton tableau. Dans ta fonction de callback tu filtres les vaisseaux qui n'ont pas de points de vie. Tu obtiendras donc un tableau avec uniquement les vaisseaux attaquables. Et là tu fais ton array_rand() dessus.

par Wells83 » 08 juin 2007, 18:46

Grumph et en bon français sa donne quoi tout ce que tu viens de dire la ^^

par titerm » 08 juin 2007, 16:26

Pas de fonction direct mais en faisant un array_rand sur un array_filter et en définissant une fonction de callback qui fera le filtre sur la ou les propriétés que tu souhaites, y a pas de soucis

par Wells83 » 08 juin 2007, 16:15

Tient d'ailleurs puisqu'on est à parlé de ca. Existe t'il une fonction complémentaire pour tirer une valeur dans un tableau en fonction d'un test.

Cas pratique: je veux choisir une cible sur la quelle tiré, mais tant qu'a faire il faut que la cible est + de 0 PV (on tire rarement sur les morts)

par titerm » 08 juin 2007, 14:47

Aucun soucis pour utiliser array_rand sur un objet de type ArrayObject, il suffit juste de caster
$collection = new ArrayObject();
// ... $collection.append(ships);
$elected = array_rand((array) $collection);

$collection[$elected].fire();

par Wells83 » 08 juin 2007, 14:33

Actuellement je fait tout avec des tableaux multidimensionnel. Ca marche impeccables. Le problème est donc purement de lisibilité. C'est quand même plus simple de faire:

vaisseau12.coque-=vaisseau1005.arme3.degat;

que

$vso_a[$id_vso_touche]-=$degats_armes[$id_armes];


Ensuite j'ai besoin de collection car il me faut tirer les cibles au hasard parmi une liste de vaisseaux disponible. Et pour cela la fonction rand des tableaux est trés pratique. Je m'inquiétais donc de savoir si cela serait tjs possible avec de l'objet.

par Sékiltoyai » 08 juin 2007, 01:11

Ensuite, je pense qu'on met plus en péril la maintenabilité du code en forçant des concepts étrangers qu'en utilisant la POO de PHP 5. Je ne sais pas si en Java ou C++ instantier 2000 objets est une bonne pratique, mais je sais qu'en PHP ça ne l'est pas.
La réponse est simple, oui, dans les langages bien fait, c'est une bonne pratique puisqu'un objet n'est pas sensé prendre plus de place que les variables réelles, puisque normalement, dans un obje, on ne stocke pas les noms de variable, et évidemment pas les fonctions non plus...
D'autant plus que dans le jeu les 1000 vaisseaux en question ne font pas tous quelque chose, on n'a besoin de n'instancier que ceux qui vont faire quelque chose mais alors le concept d'une collection d'objet (pour le peu que j'en comprends) perd de son attrait.
Conceptuellement ce n'est pas un erreur. Disons que ca risque de devenir lourd s'il doit stocker toutes ses variables dans des tableaux multidimentionnels, par contre créer un objet pour stocker tous les objets, je conviens que c'est une erreur, puisque ce n'est pas du tout nécessaire...