Bonjour, je me suis fait un petit logiciel pour un projet personnel de graphisme. Celui-ci consiste a créé une suite de 8 chars composés de 0 et 1 binaire de 13 lignes. Ça cava, mais ça donne ça :
on voit que les lignes ce ressemble beaucoup j'aimerais qu'il soie plus aléatoire.
Voici mon code Csharp:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace binaryGen
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string[] line = new string[13];
int y;
private void Generater()
{
var binary = "01";
var random = new Random();
var resultat = new string(
Enumerable.Repeat(binary, 15)
.Select(s => s[random.Next(s.Length)])
.ToArray());
line[y] = resultat;
}
private void BT_GEN_Click(object sender, EventArgs e)
{
for (y = 0; y < 13; y++)
{
Generater();
TB_OUT.Lines = line ;
}
}
}
}
Tu te sers mal de l'objet ramdom. Un bien meilleur design serais d'avoir une instance de random en membre privé de ta classe. Tu l'initialise une fois dans le constructeur et tu t'en sert à chaque appel de BT_GEN_Click.
Au passage tu n'a même pas fait l'effort d'aller lire la doc :
Sors l'instanciation du Random de ta fonction et ça marchera beaucoup mieux
Il faut comprendre qu'à sa création, une instance de Random se sert de l'heure système pour initialiser sa séquence de nombres pseudos aléatoires.
Or les différentes itérations de ta méthode Générater (Générer serait plus français) se produisent tellement vite que tout tes Random sont initialisés avec la même heure donc avec la même séquence de nombres.
De plus utiliser y et line comme membre de la classe et peu utile (judicieux) il vaudrait mieux (pour y) le déclarer localement et faire en sorte (pour line) que ta méthode renvoie un char[] (ou un string[] ou un string ou autre).
Au passage tes lignes ne font pas 8 mais 15 caractères
Exemple (avec une méthode de génération légèrement différente):
private static readonly Random random = new Random();
private string BinDigits(int size)
{
// comme random.Next() renvoie un nombre compris 0 et int.MaxValue
// il a (en gros) 1 chance sur 2 d'être pair ou impair
// donc que le reste de sa division par 2 soit 0 ou 1
return Enumerable
.Range(1, size)
.Aggregate("", (accumulator, ignoreMe) => accumulator + random.Next() % 2);
}
// ensuite on peut récupérer ses lignes comme ceci
string lines = Enumerable
.Range(1, 13)
.Aggregate("", (accumulator, ignoreMe) => accumulator + BinDigits(15) + Environment.NewLine);
On pourrait aussi faire une séquence infinie de chiffres binaires (à l'aide d'un block itérateur) :
private static readonly Random random = new Random();
private IEnumerable<int> BinDigits()
{
while (true)
{
yield return random.Next() % 2;
}
}
// pour récupérer ses lignes :
string lines = Enumerable
.Range(1, 13)
.Aggregate("", (accLines, ignore) =>
accLines + BinDigits()
.Take(15) // ne surtout pas l'oublier sinon on récupère des chiffres longtemps
.Aggregate("", (accLine, currentDigit) =>
accLine + currentDigit) + Environment.NewLine);
J'ai volontairement épargné les versions avec StringBuilder (dans le cas où il y a beaucoup de concaténations) par souci de simplicité.
En parlant de simplicité on peut aussi bien faire (sans StringBuilder toujours)
private string BinDigitsGrid(int rows, int columns)
{
// ici on "pourrait" mettre le random ici parce qu'il n'est pas réinitialisé pendant l'exécution de toute la grille
// mais attention si la méthode doit être appelée fréquemment (risque de recréer le problème initial)
Random random = new Random();
string result = "";
for (int r = 0; r < rows; ++r)
{
for (int c = 0; c < columns; ++c)
{
result += random.Next() % 2;
}
result += Environment.NewLine;
}
return result;
}
// exemple d'utilisation
string lines = BinDigitsGrid(13, 15);
Bon on pourrait encore jouer longtemps à trouver d'autres méthodes
Cordialement !
Arf je fais un gros pâté et voilà qu'entre temps je me fais griller
Censément, quelqu'un de sensé est censé s'exprimer sensément.
Ouch Sehnsucht, les Aggregate() pour concaténer des strings c'est élégant mais très peu performant - ça crée des strings intermédiaires en pagaille. Utilise plutôt la méthode string.Join() pour avoir le même résultat - dommage qu'il n'y ait pas nativement d'extension JoinString() pour les IEnumerable<>, mais elle est facile à créer.
Même problème dans ta méthode BinDigitsGrid() : ça parait plus simple (et encore), mais en terme de perfs c'est catastrophique. Les StringBuilders ont une utilité bien réelle.
La solution préconisée serait plutôt celle-ci :
private string BinDigitsGrid(int rows, int columns)
{
// ici on "pourrait" mettre le random ici parce qu'il n'est pas réinitialisé pendant l'exécution de toute la grille
// mais attention si la méthode doit être appelée fréquemment (risque de recréer le problème initial)
Random random = new Random();
StringBuilder result = new StringBuilder();
for (int r = 0; r < rows; ++r)
{
for (int c = 0; c < columns; ++c)
{
result.Append(random.Next() % 2);
}
result.Append(Environment.NewLine);
}
return result.ToString();
}
// exemple d'utilisation
string lines = BinDigitsGrid(13, 15);
Ou encore, pour avoir une plus grande flexibilité :
private void BinDigitsGrid(TextWriter writer, int rows, int columns)
{
// ici on "pourrait" mettre le random ici parce qu'il n'est pas réinitialisé pendant l'exécution de toute la grille
// mais attention si la méthode doit être appelée fréquemment (risque de recréer le problème initial)
Random random = new Random();
for (int r = 0; r < rows; ++r)
{
for (int c = 0; c < columns; ++c)
{
writer.Write(random.Next() % 2);
}
writer.WriteLine();
}
}
// exemples d'utilisation :
//1) pour avoir une string
StringBuilder builder = new StringBuilder();
BinDigitsGrid(new StringWriter(builder), 13, 15);
string result = builder.ToString();
//2) pour écrire directement dans la console
BinDigitsGrid(Console.Out, 13, 15);
//3) pour écrire directement dans un fichier
using(var writer = new StreamWriter("output.txt"))
BinDigitsGrid(writer, 13, 15);
J'ai quand même bien précisé que je mettais pas les StringBuilder par souci de simplicité (sinon c'est clair que ça plombe pas mal les choses)
Par contre entre String.Join et Aggregate(new StringBuilder() ...) il y a beaucoup de différences ?
Censément, quelqu'un de sensé est censé s'exprimer sensément.
Aggregate with string.Empty 92 ticks
Aggregate with StringBuilder 15 ticks
Join with Select 38 ticks
For loop with StringBuilder 13 ticks
For loop with List<char> 39 ticks
Code des méthodes :
private static string GetRandomString_StringAggregate( int length )
{
return Enumerable.Range( 0, length ).Aggregate( string.Empty, ( s, _ ) => s + GetRandomChar() );
}
private static string GetRandomString_BuilderAggregate( int length )
{
return Enumerable.Range( 0, length ).Aggregate( new StringBuilder(), ( b, _ ) => b.Append( GetRandomChar() ) ).ToString();
}
private static string GetRandomString_JoinSelect( int length )
{
return string.Join( string.Empty, Enumerable.Range( 0, length ).Select( n => GetRandomChar() ) );
}
private static string GetRandomString_ForBuilder( int length )
{
var builder = new StringBuilder();
for ( int n = 0; n < length; n++ )
{
builder.Append( GetRandomChar() );
}
return builder.ToString();
}
private static string GetRandomString_ForCharList( int length )
{
var list = new List<char>();
for ( int n = 0; n < length; n++ )
{
list.Add( GetRandomChar() );
}
return string.Join( string.Empty, list );
}
private static char GetRandomChar()
{
return (char) RandGen.Next( 'A', 'z' );
}
Code du benchmark :
private static void Main( string[] args )
{
const int iterations = 50000;
const int length = 150;
var actions = new Dictionary<string, Action>
{
{ "Aggregate with string.Empty", () => GetRandomString_StringAggregate(length) },
{ "Aggregate with StringBuilder", () => GetRandomString_BuilderAggregate(length) },
{ "Join with Select", () => GetRandomString_JoinSelect(length) },
{ "For loop with StringBuilder", () => GetRandomString_ForBuilder(length) },
{ "For loop with List<char>", () => GetRandomString_ForCharList(length) }
};
Benchmark( actions, iterations );
Console.WriteLine( "Done." );
Console.ReadKey();
}
private static void Benchmark( Dictionary<string, Action> actions, int iterations )
{
foreach ( var pair in actions )
{
var ticks = Benchmark( pair.Value, iterations );
Console.WriteLine( "{0,-30} {1,3} ticks", pair.Key, ticks );
}
}
// gets number of ticks from method
private static long Benchmark( Action action, int iterations )
{
long totalTicks = 0;
var watch = new Stopwatch();
for ( int n = 0; n < iterations; n++ )
{
watch.Restart();
action();
watch.Stop();
totalTicks += watch.ElapsedTicks;
}
return totalTicks / iterations;
}
Merci à tous, je ne pensais pas provoquer au temps de controverse et de réponse. Je vais faire des tests avec vos idées.
J'ai utilisé cette méthode:
private string BinDigitsGrid(int rows, int columns)
{
// ici on "pourrait" mettre le random ici parce qu'il n'est pas réinitialisé pendant l'exécution de toute la grille
// mais attention si la méthode doit être appelée fréquemment (risque de recréer le problème initial)
Random random = new Random();
StringBuilder result = new StringBuilder();
for (int r = 0; r < rows; ++r)
{
for (int c = 0; c < columns; ++c)
{
result.Append(random.Next() % 2);
}
result.Append(Environment.NewLine);
}
return result.ToString();
}
// exemple d'utilisation
string lines = BinDigitsGrid(13, 15);
Que j'ai adapté à ma windowform ça fonctionne très bien merci encore.
Encore plus rapide : (7 ticks chez moi, moitié moins du for sur StringBuilder)
private static string GetRandomString_ForCharArray( int length )
{
var tab = new char[length];
for ( int n = 0; n < length; n++ )
{
tab[n] = GetRandomChar();
}
return new string( tab );
}
// penser à activer le code unsafe dans les propriétés du projet
private static unsafe string GetRandomString_WhileCharPointer(int length)
{
char* pTab = stackalloc char[length];
while (length > 0)
pTab[--length] = GetRandomChar();
return new string(pTab);
}
Note: c'est pas dit que ça soit beaucoup plus rapide (un peu peut-être sur le while mais pas plus, et encore si ça se trouve c'est optimisé par le compilo justement)
Cordialement !
Censément, quelqu'un de sensé est censé s'exprimer sensément.
Effectivement, le JIT enlève les vérifications inutiles donc ton code est plus rapide quand il est lancé depuis VS mais produit le même résultat hors de VS.
[C# 4.0] random char pas si random
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
on voit que les lignes ce ressemble beaucoup j'aimerais qu'il soie plus aléatoire.