c# Generate Random number passing long as a seed instead of int32, but l need to pass phone numbers or accounts number
https://learn.microsoft.com/en-us/dotnet/api/system.random.-ctor?view=netframework-4.8#System_Random__ctor_System_Int32_
Please suggest any reliable NuGet package which does this or any implementation who has already done something like this.
I need to pass the complete PhoneNumber as the seed which I'm able to do in python but not with C# and my code stack is all in C#
using System;
public class Program
{
public static void Main()
{
int seed = 0123456789;
Random random = new Random(seed);
double result = random.NextDouble();
Console.WriteLine(result);
}
}
Some insights on my requirements and what I'm trying to achieve:
1)We're doing this for A/B testing and todo data analysis on the
experience of two services.
2)When a request comes with
phoneNumber based on random.NextDouble() there is a preset percentage
which we use to determine whether to send a request to service A or
service B
3)For example, let's says the request comes and falls
under >0.5 then we direct the request to service A and the next time
the request with the same phone number comes in it will be >0.5 and
goes service A since the seed is a unique hash of phoneNumber.
The method GetHashCode() belongs to Object class, it has nothing to do with random number generation. Please read here (https://learn.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=netframework-4.8). The documentation clearly states that it is possible to get collisions specially if input is consistent.
The method HashAlgorithm.ComputeHash (documented here - https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.hashalgorithm.computehash?view=netframework-4.8) calculates the hash for a given value, but it is consistent in nature, i.e. if input is same, generated output is also same. Obviously this is not the desired output (I assume). I have attached the sample code I tried to generate this.
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
while (true)
{
Console.WriteLine("Enter a 9 digit+ number to calculate hash");
var val = Console.ReadLine();
long target = 0;
bool result = long.TryParse(val,out target);
if (result)
{
var calculatedHash = OutputHash(target);
Console.WriteLine("Calculated hash is : " + calculatedHash);
}
else
{
Console.WriteLine("Incorrect input. Please try again.");
}
}
}
public static string OutputHash(long number)
{
string source = Convert.ToString(number);
string hash;
using (SHA256 sha256Hash = SHA256.Create())
{
hash = GetHash(sha256Hash, source);
Console.WriteLine($"The SHA256 hash of {source} is: {hash}.");
Console.WriteLine("Verifying the hash...");
if (VerifyHash(sha256Hash, source, hash))
{
Console.WriteLine("The hashes are the same.");
}
else
{
Console.WriteLine("The hashes are not same.");
}
}
return hash;
}
private static string GetHash(HashAlgorithm hashAlgorithm, string input)
{
// Convert the input string to a byte array and compute the hash.
byte[] data = hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(input));
// Create a new Stringbuilder to collect the bytes
// and create a string.
var sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
// Return the hexadecimal string.
return sBuilder.ToString();
}
// Verify a hash against a string.
private static bool VerifyHash(HashAlgorithm hashAlgorithm, string input, string hash)
{
// Hash the input.
var hashOfInput = GetHash(hashAlgorithm, input);
// Create a StringComparer an compare the hashes.
StringComparer comparer = StringComparer.OrdinalIgnoreCase;
return comparer.Compare(hashOfInput, hash) == 0;
}
I agree with #Knoop 's comment above that you might end up with same integer mapping to multiple long number input values.
If you are looking for a 'pure' random number generator with long value as seed, you don't have a choice but to go for third party libraries (or implementing your own custom algorithm). However, rather than getting into such complexities, simple
Guid g = Guid.NewGuid();
should do the trick (https://learn.microsoft.com/en-us/dotnet/api/system.guid.newguid?view=netframework-4.8).
Documentation (https://learn.microsoft.com/en-gb/windows/win32/api/combaseapi/nf-combaseapi-cocreateguid?redirectedfrom=MSDN )says that even this can end up having collisions but chances are very minimal.
Finally, this sounds like potential duplicate of .NET unique object identifier
take the hash of the phone number, eg:
var phoneNumber = 123456789L;
var seed = phoneNumber.GetHashCode();
This means that for the same phoneNumber you will get the same sequence. It also means that for some phone numbers you will get identical sequences, but that is going to be slim. And it might be different on different .net runtimes as commented, but you might not care.
Not sure why you want to, but I there are reasons, e.g. test code
Related
I need my app to handle a list of mods from a database and a list of locally downloaded mods that aren't.
Each mod of the database has a unique uint ID that I use to identify him but local mods don't have any ID.
At first I tried to generate an ID with string.GetHashCode() by using the mod's name but GetHashCode is still randomized at each run of the app.
Is there any other way to generate a persistent uint ID from the mod's name ?
Current code :
foreach(string mod in localMods)
{
//This way I get a number between 0 and 2147483648
uint newId = Convert.ToUInt32(Math.Abs(mod.GetHashCode());
ProfileMod newMod = new ProfileMod(newId);
}
The method GetHashCode() doesn't return the same value for the same string, especially if you re-run the application. It has a different purpose (like checking the equality during runtime, etc.).
So, it shouldn't be used as a unique identifier.
If you'd like to calculate the hash and get consistent results, you might consider using the standard hashing algorithms like MD5, SHA256, etc.
Here is a sample that calculates SHA256:
using System;
using System.Security.Cryptography;
using System.Text;
public class Program
{
public static void Main()
{
string input = "Hello World!";
// Using the SHA256 algorithm for the hash.
// NOTE: You can replace it with any other algorithm (e.g. MD5) if you need.
using (var hashAlgorithm = SHA256.Create())
{
// Convert the input string to a byte array and compute the hash.
byte[] data = hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(input));
// Create a new Stringbuilder to collect the bytes
// and create a string.
var sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
// Return the hexadecimal string.
var hash = sBuilder.ToString();
Console.WriteLine($"The SHA256 hash of {input} is: {hash}.");
}
}
}
Though SHA256 produces longer result than MD5, the risk of the collisions are much lower. But if you still want to have smaller hashes (with a higher risk of collisions), you can use MD5, or even CRC32.
P.S. The sample code is based on the one from the Microsoft's documentation.
So I ended up listening to your advises and found a good answer in another post by using SHA-1
private System.Security.Cryptography.SHA1 hash = new System.Security.Cryptography.SHA1CryptoServiceProvider();
private uint GetUInt32HashCode(string strText)
{
if (string.IsNullOrEmpty(strText)) return 0;
//Unicode Encode Covering all characterset
byte[] byteContents = Encoding.Unicode.GetBytes(strText);
byte[] hashText = hash.ComputeHash(byteContents);
uint hashCodeStart = BitConverter.ToUInt32(hashText, 0);
uint hashCodeMedium = BitConverter.ToUInt32(hashText, 8);
uint hashCodeEnd = BitConverter.ToUInt32(hashText, 16);
var hashCode = hashCodeStart ^ hashCodeMedium ^ hashCodeEnd;
return uint.MaxValue - hashCode;
}
Could probably be optimized but it's good enough for now.
I wouldn't trust any solution involving hashing or such. Eventually you will end-up having conflicts in the IDs especially if you have huge amount of records on your DB.
What I would prefer to do is to cast the int ID of the DB to a string when reading it and then use some function like Guid.NewGuid().ToString() to generate a string UID for the local ones.
This way you will not have any conflict at all.
I guess that you will have to employ some kind of such strategy.
Our user recieve emails with a prepared hyperlink.
This hyperlink has a parameter.
When the user clicks the hyperlink I need to make sure that the user did not tamper with the parameter.
The parameter is a simple integer.
I would like to create a hash from the integer.
I would like to sign the hash.
What are the current .NET classes I should use for this task?
The easiest way to do this is use a HMAC ("keyed-hash message authentication code").
public static string CreateHMAC(int data, byte[] key)
{
using(var hmac = new HMACSHA1(key))
{
var dataArray = BitConverter.GetBytes(data);
var resultArray = hmac.ComputeHash(dataArray);
return Convert.ToBase64String(resultArray);
}
}
You keep key secret on your server, and pass data and the result of CreateHMAC to the user in the URL. When the user clicks the link you verify that the data was not modified.
public static bool VerifyHMAC(int data, byte[] key, string verification)
{
using(var hmac = new HMACSHA1(key))
{
var dataArray = BitConverter.GetBytes(data);
var computedHash = hmac.ComputeHash(dataArray);
var verificationHash = Convert.FromBase64String(verification);
for (int i = 0; i < verificationHash.Length; i++)
{
if (computedHash[i] != verificationHash[i])
{
return false;
}
}
return true;
}
}
You can see here how to calculate the hash.
Since HASH functions accepts only ARRAYS, I advice you to create an array of ONE single item, with your integer there.
You may "sign" the input calculating the HASH of the calculated HASH above PLUS a constant of your application (or date and hour, for instance)
I have the following String:
String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
I need to create two strings from it:
A string obtained simply by reordering the characters;
A string obtained by selecting 10 characters and reordering them.
So for (1) I would get, for example:
String characters = "jkDEF56789hisGHIbdefpqraXYZ1234txyzABCcglmnoRSTUVWuvwJKLMNOPQ0";
And for (2) I would get, for example:
String shortList = "8GisIbH9hd";
THE PROBLEM
I could just change to Char Array and order by randomly by a Guid.
However I want to specify some kind of key (maybe a guid?) and for that key the result or reordering and of selecting the shortList must be the same.
Does this make sense?
you could convert your GUID string to an int array of its ascii/utf/whatever codes like here
Getting The ASCII Value of a character in a C# string.
then iterate over this array with something along lines of this (note: this is pseudocode):
string res="";
for(elem in intconvertedGUIDstring) res+= characters[elem%(characters.count)];
for the task [2] you could reverse your Characters i.e. like here Best way to reverse a string
and use the c# string function left() to truncate it before running it through the same procedure
You can use a hash function with a good distribution value as seed for comparison between elements. Here's a sample:
static ulong GetHash(char value, ulong seed)
{
ulong hash = seed * 3074457345618258791ul;
hash += value;
hash *= 3074457345618258799ul;
return hash;
}
And use this function for comparison:
static void Main()
{
var seed = 53ul;
var str = "ABCDEFHYUXASPOIMNJH";
var shuffledStr = new string(str.OrderBy(x => GetHash(x, seed)).ToArray());
Console.WriteLine(shuffledStr);
}
Now every time you order by seed 53 you'll get the same result, and if you seed by 54 you'll get a different result.
am started testing hash function on the uniqueness of the generated HashCodes with my algorithm. And i wrote next text class to test when the same hashCode will be generated.
class Program
{
static void Main(string[] args)
{
var hashes = new List<int>();
for (int i = 0; i < 100000; i++)
{
var vol = new Volume();
var code = vol.GetHashCode();
if (!hashes.Contains(code))
{
hashes.Add(code);
}
else
{
Console.WriteLine("Same hash code generated on the {0} retry", hashes.Count());
}
}
}
}
public class Volume
{
public Guid DriverId = Guid.NewGuid();
public Guid ComputerId = Guid.NewGuid();
public int Size;
public ulong VersionNumber;
public int HashCode;
public static ulong CurDriverEpochNumber;
public static Random RandomF = new Random();
public Volume()
{
Size = RandomF.Next(1000000, 1200000);
CurDriverEpochNumber ++;
VersionNumber = CurDriverEpochNumber;
HashCode = GetHashCodeInternal();
}
public int GetHashCodeInternal()
{
unchecked
{
var one = DriverId.GetHashCode() + ComputerId.GetHashCode() * 22;
var two = (ulong)Size + VersionNumber;
var result = one ^ (int)two;
return result;
}
}
}
GUIDs fields DriverId, ComputerId and int Size are random.
I assumed that at some time we will generate the same hash-code. You know it will break work with big collections. Magic was in fact that the retry number when the duplicated
hash code is generated are the same! I run sample code for several time and got near the same result: firs run duplicate on 10170 retry, second on 7628, third 7628
and again and again on 7628. Some times i got a little bit others results. Bu in most cases it was on 7628.
It has no explanations for me.
Is it error in . NET random generator or what?
Thanks all. Now it is clear the was bug in my code (Matthew Watson). I had to call GetHashCodeIntelrnal() and not GetHashCode(). The best GetHashCode unique results gave me:
public int GetHashCodeInternal()
{
unchecked
{
var one = DriverId.GetHashCode() + ComputerId.GetHashCode();
var two = ((ulong)Size) + VersionNumber;
var result = one ^ (int)two << 32;
return result;
}
}
Bu still on near 140 000 it give same code... i think it is not good because ve have collections near 10 000...
If you change your Console.WriteLine() to also print Volume.Size like so:
Console.WriteLine("Same hash code generated on the {0} retry ({1})", hashes.Count, vol.Size);
you will see that although hashes.Count is always the same for the first collision, vol.Size is usually different.
This seems to rule out the random number generator causing this issue - it looks like some strange property of GetHashCodeInternal().
Closer inspection reveals that you are calling the wrong hash code function.
This line: var code = vol.GetHashCode();
Should be: var code = vol.HashCode;
Try that instead! Because at the moment you are calling the default .Net GetHashCode() which is not doing what you want at all.
You will need to pass in the random number generator, having created a single one to be reused, as currently you're creating new instances of them too close together which results in the same seed being used, and hence the same sequence of numbers coming out.
Your results will randomly come out seemingly random at points where the seed is generated from the next ticks/seconds of the seed date. So, just incidental, really.
In the application, when special types of objects are created, I need to generate a unique-id for each of them. The objects are created thro' a factory and have a high possibility of being created in a 'bulk' operation. I realize that the "Random" from the framework is not so 'random' after all, so I tried appending the time-stamp as follows:
private string GenerateUniqueId()
{
Random randomValue = new Random();
return DateTime.Now.Ticks.ToString() + randomValue.Next().ToString();
}
Unfortunately, even this does not work. For objects that are created in rapid succession, I generate the same Unique Id :-(
Currently, I am implementing it in a crude way as follows:
private string GenerateUniqueId()
{
Random randomValue = new Random();
int value = randomValue.Next();
Debug.WriteLine(value.ToString());
Thread.Sleep(100);
return DateTime.Now.Ticks.ToString() + value.ToString();
}
Since this is not a very large application, I think a simple and quick technique would suffice instead of implementing an elaborate algorithm.
Please suggest.
A GUID is probably what you're looking for:
private string GenerateUniqueId()
{
return Guid.NewGuid().ToString("N");
}
If you want a smaller, more manageable ID then you could use something like this:
private string GenerateUniqueId()
{
using (var rng = new RNGCryptoServiceProvider())
{
// change the size of the array depending on your requirements
var rndBytes = new byte[8];
rng.GetBytes(rndBytes);
return BitConverter.ToString(rndBytes).Replace("-", "");
}
}
Note: This will only give you a 64-bit number in comparison to the GUID's 128 bits, so there'll be more chance of a collision. Probably not an issue in the real world though. If it is an issue then you could increase the size of the byte array to generate a larger id.
Assuming you do not want a GUID, First option would be a static field, and interlocked:
private static long lastId = 0
private static long GetNextId() {
return Interlocked.Increment(ref lastId);
}
If you want something based on time ticks, remember the last value and if the same manually increment and save; otherwise just save:
private static long lastTick = 0;
private static object idGenLock = new Object();
private static long GetNextId() {
lock (idGenLock) {
long tick = DateTime.UtcNow.Ticks;
if (lastTick == tick) {
tick = lastTick+1;
}
lastTick = tick;
return tick;
}
}
(Neither of these approaches will be good with multiple processes.)
In your comments Codex you say use the unique ID as a file name. There is a specific function for generating cryptographically secure file names, Path.GetRandomFileName()
As it's cryptographically secure these would be unique even in batch operations. The format is a little horrible though as they're optimised for filenames, but it may work for other references as well.
Why can't your factory (which is presumably single-threaded) generate sequential unique integers? If you expected Random() to work, why not Guid() (or whatever is equivalent)?
If you're going to resort to coding your own UUID-generator, make sure you salt the generator.
I suggest you check out the open source package ossp-uuid, which is an ISO-C API and CLI for generating Universally Unique Identifiers.