i want to get two floats from this string
string sample = "#G10F30";
( G and F can be inverted )
and store their value into the following variables
float g;
float f;
since i'll repeat this for like 100 times i'm looking to get is as fast as possible.
i can iterate through all the characters and store just the digits into a two different strings and then parse their values into a floats, but i'm wondering if any better approach is suitable.
any help?
float g;
float f;
string sample = "#G10F30";
//string sample = "#F10G30"; //vice versa
sample = sample.Replace("#", string.Empty);
var splitG = sample.Split("G",StringSplitOptions.RemoveEmptyEntries);
switch (splitG.Length)
{
case 1:
var splitF = splitG[0].Split("F");
g = float.Parse(splitF[0]);
f = float.Parse(splitF[1]);
break;
case 2:
f = float.Parse(splitG[0].Replace("F",string.Empty));
g = float.Parse(splitG[1]);
break;
default:
throw new Exception("Incorrect input string");
}
Console.WriteLine(f);
Console.WriteLine(g);
To elaborate the proposed Regex sulution:
float g = float.NaN;
float f = float.NaN;
string sample = "#G10F30";
foreach (Match m in Regex.Matches(sample, #"(?<var>[FG])(?<val>[+-]?\d+(\.\d*)?)"))
{
var variable = m.Groups["var"].Value;
var value = float.Parse(m.Groups["val"].Value);
switch (variable)
{
case "F": f = value; break;
case "G": g = value; break;
}
}
Console.WriteLine($"f={f}, g={g}");
(?<var>[FG]) will match F or G and assign it to the group "var".
(?<val>[+-]?\d+(\.\d*)?)will match a floating point number and assign it to the group "val".
Note: The regex for matching floating point numbers is a bit limited and could be extended for your requirements, see Regular expression for floating point numbers
You can always write a parser:
(float? f, float? g) ParseTwoFloats(string input)
{
if (!input.StartsWith('#')) return (null, null);
var currentBlock = default(char?);
var currentStartIndex = default(int?);
var f = default(float?);
var g = default(float?);
for (var index = 1; index < input.Length; index++)
{
var token = input[index];
switch (token)
{
case 'F':
case 'G':
{
if (currentBlock.HasValue) UpdateWithNew(index);
currentBlock = token;
currentStartIndex = index;
break;
}
}
}
if (currentBlock.HasValue) UpdateWithNew(input.Length - 1);
void UpdateWithNew(int index)
{
var value = float.Parse(
input.AsSpan(currentStartIndex.Value + 1, index - currentStartIndex.Value - 1),
provider: CultureInfo.InvariantCulture);
switch (currentBlock.Value)
{
case 'F': f = value; break;
case 'G': g = value; break;
}
}
return (f, g);
}
This does no heap allocations and is pretty easy to extend based on your other needs (e.g. adding another thing to parse, things to skip, proper error handling etc.).
If you want something even simpler, you can always do something like this:
(float f, float g) ParseTwoFloats(string input)
{
return (FindByToken(input, 'F', 'G'), FindByToken(input, 'G', 'F'));
float FindByToken(string input, char startToken, char endToken) =>
input.IndexOf(startToken) is {} start
? Parse(input, start + 1, input.IndexOf(endToken, start))
: 0f;
float Parse(string input, int start, int end) =>
float.Parse(input.AsSpan(start, end == -1 ? input.Length - start : end - start),
provider: CultureInfo.InvariantCulture);
}
This is less flexible, and does involve two iterations through the string. The iteration can be faster than in the parser. As with the parser, there's no heap allocations.
You can probably do even better if you replace the string input with another ReadOnlySpan (assuming you're working on substrings of a larger string) - avoiding heap allocations can make a huge difference when parsing string data. If you can avoid creating millions of substrings, you can get a pretty nice improvement. But as always, measure first.
Related
Hello I'm working on this math game and for my last scene I did what feels like a lot of repetitive code but I'm not sure if there's a way to simplify it, I have linked below so maybe some more seasoned programmers might have some more elegant solutions!
For example, Im trying to generate every permutation of something like (a[]b)²[]c[]d where the brackets will be replaced by +,-,*, or /. What I have been doing is just creating random percent if statements to pick a specific version like "(a+b)²/c-d" Is there possibly a less "brute-force" and readable approach then what I have been doing?
if(UnityEngine.Random.Range(0,101)>50){
// 50% of being (a+b)²(c)+d
if(UnityEngine.Random.Range(0,101)>50){
ans = ((int) Mathf.Pow((float) a+ (float) b, 2))*c+d;
input.text = "("+a+"+"+b+")"+"²"+"("+c+")"+"+"+d+"=";
Debug.Log("Problem ID: 78");
// 50% of being (a+b)²(c)-d
} else {
ans = ((int) Mathf.Pow((float) a+ (float) b, 2))*c-d;
input.text = "("+a+"+"+b+")"+"²"+"("+c+")"+"-"+d+"=";
Debug.Log("Problem ID: 79");
}
// 50% of being (a-b)²(c)[]d
} else {
// 50% of being (a-b)²(c)+d
if(UnityEngine.Random.Range(0,101)>50){
ans = ((int) Mathf.Pow((float) a- (float) b, 2))*c+d;
input.text = "("+a+"-"+b+")"+"²"+"("+c+")"+"+"+d+"=";
Debug.Log("Problem ID: 80");
// 50% of being (a-b)²(c)-d
} else {
ans = ((int) Mathf.Pow((float) a- (float) b, 2))*c-d;
input.text = "("+a+"-"+b+")"+"²"+"("+c+")"+"-"+d+"=";
Debug.Log("Problem ID: 81");
}
(Pastebin below for more context)
https://pastebin.pl/view/d1bfb99e
I applaud your desire to make your code more readable. The basic idea is to split (a) defining, (b) choosing and (c) applying your operators.
Step 1: You define Operators. Each Operator combines both a mathematical operation (e.g. Add would be (a, b) => a + b) and a symbol (e.g. Add would be "+").
class Operator
{
public Func<int, int, int> Calculate { get; }
public string Symbol { get; }
public Operator(Func<int, int, int> calculate, string symbol)
{
Calculate = calculate;
Symbol = symbol;
}
}
private Operator Add = new Operator((a, b) => (a + b), "+");
private Operator Subtract = new Operator((a, b) => (a - b), "-");
Step 2: Then you randomly choose your operators (I used System.Random, since I'm not familiar with Unity, but feel free to replace it with the random number generator of your choice):
var rnd = new Random();
private (Operator op1, Operator op2, int problemId) RandomlyChooseProblem()
{
switch (rnd.Next(4))
{
case 0: return (Add, Add, 78);
case 1: return (Add, Subtract, 79);
case 2: return (Subtract, Add, 80);
case 3: return (Subtract, Subtract, 81);
default: throw new InvalidOperationException("This should not happen.");
}
}
Step 3: You apply them:
var (op1, op2, problemId) = RandomlyChooseProblem();
ans = op2.Calculate((int)Math.Pow(op1.Calculate(a, b), 2) * c, d);
input.text = $"(a{op1.Symbol}b)²*c{op2.Symbol}d");
Debug.Log($"Problem ID: {problemId}");
Adding a new operator (e.g. Multiply) or a new problem variant (e.g. (Add, Multiply, 82)) is now just a single line of code.
Break the calculation into parts - a±b, square * c and ±d. Calculate them separately, and multiply them to get the final result. For the text, you can use string interpolation
float ans;
string operator1;
string operator2;
if (UnityEngine.Random.Range(0,101)>50) {
ans = (float) a + (float) b;
operator1 = "+";
} else {
ans = (float) a - (float) b;
operator1 = "-";
}
ans = (int)(ans * ans) * c;
if (UnityEngine.Random.Range(0,101)>50) {
ans += d;
operator2 = "+";
} else {
ans -= d;
operator2 = "-";
}
input.text = $"(a{operator1}b)²(c){operator2}d";
Also note that UnityEngine.Random.Range(0,101) > 50 is not exactly 50% probability. You probably meant UnityEngine.Random.Range(1,101) > 50 instead, but I would just use UnityEngine.Random.Range(0,2) == 0.
The problem IDs can be flattened by generating 2 random bits, and adding the 2-bit number encoded by those bits to 78:
float ans;
string operator1;
string operator2;
int random1 = UnityEngine.Random.Range(0,2);
int random2 = UnityEngine.Random.Range(0,2);
if (random1 == 0) {
ans = (float) a + (float) b;
operator1 = "+";
} else {
ans = (float) a - (float) b;
operator1 = "-";
}
ans = (int)(ans * ans) * c;
if (random2 == 0) {
ans += d;
operator2 = "+";
} else {
ans -= d;
operator2 = "-";
}
int problemID = 78 + random1 + random2 * 2;
Debug.Log($"Problem ID: {problemID}");
input.text = $"(a{operator1}b)²(c){operator2}d";
This trick is not particularly readable IMO though.
How to recognize two strings when most characters are similar
I want get true in this samples
"Hello Wolrld" == "HelloWorld"
OR
"hello world!!" == "helloworld"
I know that these are not equal, But since most of the characters are the same, it is enough for me
Thanks in advance
Use this
Regex.Replace(textBox1.Text, #"[^0-9a-zA-Z]+", "").ToLower() == your string in lower case
You can compute the Levenshtein distance of the two strings (see for example this C# implementation) and then define a threshold up to which you consider the strings to be "equal".
What a reasonable threshold is depends on your requirements. Probably, defining the predicate as d <= a * Math.Min(string1.Length, string2.Length) should work, where d is the Levenshtein distance of the strings and a is a factor of "similarity" between 0 and 1. In your examples a==0.3 should work.
If you're looking for a very basic check, you can enumerate over the characters using Zip to compare them, count the matching letters, and report true if the number of matches are above a certain threshold. This won't capture it if one string is a shifted version of the other; it'll only catch letters in common at the same index.
public static class ExtensionMethods
{
public static bool FuzzyCompare(this string lhs, string rhs, float ratioRequired)
{
var matchingLetters = lhs.Zip
(
rhs,
(a,b) => a == b ? 1 : 0
)
.Sum();
return (float)matchingLetters / (float)lhs.Length > ratioRequired;
}
}
To compare two strings to see if they match on at least half of the letters, pass a ratioRequired of 0.5.
public static void Main()
{
var a = "ABCD";
var b = "ABCDEFGHI";
Console.WriteLine( a.FuzzyCompare(b, 0.5F) );
}
Output:
True
Code on DotNetFiddle
This give true if 80% words are similer Try this
String str1 = "Hello world";
String str2 = "Helloworld!!";
char[] charArray;
int per = 0;
int c = 0;
if (str1.length() > str2.length()) {
per = (str1.length() * 80) / 100; // 80% per match logic
charArray = str1.toCharArray();
for (int i = 0; i < str1.length(); i++) {
String chars = String.valueOf(charArray[i]);
if (str2.contains(chars)) {
c++;
}
}
} else {
per = (str1.length() * 80) / 100; // 80% per match logic
charArray = str2.toCharArray();
for (int i = 0; i < str2.length(); i++) {
String chars = String.valueOf(charArray[i]);
if (str1.contains(chars)) {
c++;
}
}
}
if (c >= per) {
Toast.makeText(getApplicationContext(), "true", 0).show();
} else {
Toast.makeText(getApplicationContext(), "false", 0).show();
}
I want to generate a sequence of strings between two groups that can be either 1 letter [A] -> [F] , 2 letters such as [AA] -> [CD] or any other length like 3 or 4 letters using c#.
For example I can specify the start and end values, and it will generate the sequence.
From [AA] to [CD] should generate
AA
AB
AC
AD
BA
BB
BC
BD
CA
CB
CC
CD
I tried to utilize base-26 algorithm to generate the required sequence but failed to get the required output.
string from ="AA";
string to = "CD";
IEnumerable<string> mysequence = Enumerable.Range(ConvertNumber(from), ConvertNumber(to) - ConvertNumber(from)).Select(ConvertAlpha).ToList();
public static string ConvertAlpha(int value)
{
const int a = (int)'A';
value = value - 1;
var returnValue = new StringBuilder();
while (value > -1)
{
var remainder = value % 26;
returnValue.Insert(0, (char)(a + remainder));
value = value / 26 - 1;
}
return returnValue.ToString();
}
public static int ConvertNumber(string value)
{
const int a = (int)'A' - 1;
int returnValue = 0;
foreach (var character in value.ToUpper())
{
returnValue *= 26;
returnValue += (int)character - a;
}
return returnValue;
}
Using recursion (but with brute-force rather than elegance):
static void Main(string[] args)
{
string fromStr = "AAA";
string toStr = "CDE";
List<string> outputList = new List<string>();
BuildSequence(fromStr, toStr, outputList);
outputList.ForEach(s => { Console.WriteLine(s); });
Console.ReadLine();
}
private static void BuildSequence(
string fromStr,
string toStr,
List<string> outputList,
int index = 0,
string prev = "")
{
IEnumerable<string> newStrList = Enumerable
.Range(fromStr[index], toStr[index] - fromStr[index] + 1)
.Select(c => String.Concat(prev, (char)c));
index += 1;
if (index < fromStr.Length)
{
foreach (string newStr in newStrList)
{
BuildSequence(fromStr, toStr, outputList, index, newStr);
}
}
else
{
outputList.AddRange(newStrList);
}
}
The problem you describe is harder than just converting a base-26 representation to an integer, doing some calculation, and then converting back. Your alpha strings aren't necessarily base-26. For example, you say that given AA and CD, you want to generate
AA, AB, AC, AD
BA, BB, BC, BD
CA, CB, CC, CD
What you really have is a base-4 system that uses the characters A, B, C, and D to represent the numbers 0, 1, 2, and 3. What you're saying here is that you want to generate all the 2-digit, base-4 numbers from 00 through 33.
Given CCC => DMK, it's a base-11 system using digits C through M. It's unclear from your description whether you want:
CCC, CCD, CCE ... CCK
CDC, CDD, CDE ... CDK
...
Or if you want
CCC, CCD, CCE ... CCM
CDC, CDD, CDE ... CDM
...
If you want the former, then each digit position is a different base, and things get even more complicated.
How you define things changes how you would write the code to generate the values.
Regardless or how you want to interpret your alpha values, it's clear that your base-26 conversion functions are incorrect.
Your conversion from int to string has a few errors. In particular, subtracting 1 from the value is going to give you trouble. You can see that if you were to pass 0 to the function. The result would be an empty string.
Here's a correct function:
static string ConvertToBase26(int value)
{
const int a = (int)'A';
var result = new StringBuilder();
do
{
var remainder = value % 26;
value /= 26;
result.Insert(0, (char)(a + remainder);
} while (value > 0);
return result.ToString();
}
Your conversion from base26 to integer has similar errors, due to subtracting 1 from things. Remember, A acts like 0. The corrected function:
static int ConvertFromBase26(string value)
{
const int a = (int)'A';
int result = 0;
foreach (var c in value.ToUpper())
{
result = (result * 26) + (c - a);
}
return result;
}
I recommend renaming your base conversion functions. Your ConvertAlpha function converts an integer to a base-26 string. That's not at all clear in the name, as one could misconstrue "ConvertAlpha" to mean "ConvertFromAlpha". I would recommend ConvertToBase26 and ConvertFromBase26, which are much more explicit and unambiguous.
I am currently having issues reassigning a value to a character array. Below is my code (unfinished solution to find the next smallest palindrome):
public int nextSmallestPalindrome(int number)
{
string numberString = number.ToString();
// Case 1: Palindrome is all 9s
for (int i = 0; i < numberString.Length; i++)
{
if (numberString[i] != '9')
{
break;
}
int result = number + 2;
return result;
}
// Case 2: Is a palindrome
int high = numberString.Length - 1;
int low = 0;
bool isPalindrome = true;
for (low = 0; low <= high; low++, high--)
{
if (numberString[low] != numberString[high])
{
isPalindrome = false;
break;
}
}
char[] array = numberString.ToCharArray();
if (isPalindrome == true)
{
// While the middle character is 9
while (numberString[high] == '9' || numberString[low] == '9')
{
array[high] = '0';
array[low] = '0';
high++;
low--;
}
int replacedvalue1 = (int)Char.GetNumericValue(numberString[high]) + 1;
int replacedvalue2 = (int)Char.GetNumericValue(numberString[low]) + 1;
StringBuilder result = new StringBuilder(new string(array));
if (high == low)
{
result[high] = (char)replacedvalue1;
}
else
{
Console.WriteLine(result.ToString());
result[high] = (char)replacedvalue1;
Console.WriteLine(result.ToString());
result[low] = (char)replacedvalue2;
}
return Int32.Parse(result.ToString());
}
else return -1;
}
Main class runs:
Console.WriteLine(nextSmallestPalindrome(1001));
This returns 1001, then 101 and then gives a formatexception at the return Int32.Parse(result.ToString()); statement.
I am very confused, as I believe "result" should be 1101 after I assign result[high] = (char)replacedvalue1;. Printing replacedvalue1 gives me "1" as expected. However, debugging it line by line shows that "1001" turns into "1 1" at the end, signifying strange characters.
What could be going wrong?
Thanks
Characters and numbers aren't the same thing. I find it easiest to keep an ASCII chart open when doing this sort of thing.
If you look at one of those charts, you'll see that the character 0 actually has a decimal value of 48.
char c = (char)48; // Equals the character '0'
The reverse is also true:
char c = '0';
int i = (int)c; // Equals the number 48
You managed to keep chars and ints separate for the most part, but at the end you got them mixed up:
// Char.GetNumericValue('0') will return the number 0
// so now replacedvalue1 will equal 1
int replacedvalue1 = (int)Char.GetNumericValue(numberString[high]) + 1;
// You are casting the number 1 to a character, which according to the
// ASCII chart is the (unprintable) character SOH (start of heading)
result[high] = (char)replacedvalue1;
FYI you don't actually need to cast a char back-and-forth in order to perform operations on it. char c = 'a'; c++; is valid, and will equal the next character on the table ('b'). Similarly you can increment numeric characters:
char c = '0'; c++; // c now equals '1'
Edit: The easiest way to turn an integer 1 into the character '1' is to "add" the integer to the character '0':
result[high] = (char)('0' + replacedvalue1);
Of course there are much easier ways to accomplish what you are trying to do, but these techniques (converting and adding chars and ints) are good tools to know.
You do not have write that much code to do it.
Here is your IsPalindrome method;
private static bool IsPalindrome(int n)
{
string ns = n.ToString(CultureInfo.InvariantCulture);
var reversed = string.Join("", ns.Reverse());
return (ns == reversed);
}
private static int FindTheNextSmallestPalindrome(int x)
{
for (int i = x; i < 2147483647; i++)
{
if (IsPalindrome(i))
{
return i;
}
}
throw new Exception("Number must be less than 2147483647");
}
This is how you call it. You do not need an array to call it. You can just enter any number which is less than 2147483647(max value of int) and get the next palindrome value.
var mynumbers = new[] {10, 101, 120, 110, 1001};
foreach (var mynumber in mynumbers)
{
Console.WriteLine(FindTheNextPalindrome(mynumber));
}
I'm currently building a converter in C#. My program is converting from/to Decimal, Binary and Hexadecimal. My problem is that in some specific cases the result is not correct.
Examples:
FFFFF (hexa) to Decimal = 148575 (Real answer: 1048575)
20000 (decimal) to Decimal = 20 (Real answer: dont need a calculator :P)
Also I can't use any Convert.ToString as it is for school and my teacher asked us to manipulate the variables and play with functions.
I think my convertToString() function causes the problem of losing zeros somewhere.
private string convertToString(int value)
{
string result = "";
string tmp = "";
int y = 0;
while (value != 0) {
int valeur = value;
y = 0;
while (valeur > 9) {
valeur = valeur / 10;
y++;
}
switch (valeur) {
case 0:
tmp = "0";
break;
case 1:
tmp = "1";
break;
case 2:
tmp = "2";
break;
case 3:
tmp = "3";
break;
case 4:
tmp = "4";
break;
case 5:
tmp = "5";
break;
case 6:
tmp = "6";
break;
case 7:
tmp = "7";
break;
case 8:
tmp = "8";
break;
case 9:
tmp = "9";
break;
}
value = (int)(value - (valeur * Math.Pow(10, y)));
result = result + tmp;
}
if (y != 0) {
result = result + "0";
}
return result;
}
Thank you in advance.
I would advise you to step through your code in the debugger, examining the flow of control. You should also examine values of the variables using the Locals window. You will see where the algorithm you created is different from the algorithm you thought you created.
Another useful technique is to break the existing method into smaller methods that perform simpler parts of the task. Give the methods names that describe the part of the task they're doing. This makes it easier to change your logic (for example, when you learn about a better way to convert digits to characters!). Good luck.
Take a simple number like value=123 or value=103 and execute the code step by step.
You can do this using debugger single stepping and watching the values of the variables, but if you want to really learn how the code works, use your own brain as the CPU and see if you can work through it on a piece of paper.
As you step through the operations you will be able to see what happens to the numbers and can watch the exact moment wheer it goes wrong, in order to come to an understanding of what is happening.
The problem is that you create confusion with two names meaning the same in English and French: value / valeur;
You would calculate the decimal places like this
string result = "";
bool minus = false;
if (value == 0 ) {
result = "0";
} else {
// Begin the string with a minus (`-`) if value is negative
// and continue the conversion with a positive value.
if (value < 0) {
minus = true;
value = - value;
}
// Add decimals to the result as long as the remaining number is not zero.
while (value > 0) {
// Get last decimal using the modulo operator
int lastDecimal = value % 10;
// Prepend the decimal to the result after having converted it to a character
result = (char)('0' + lastDecimal) + result;
// Divide the value by 10. Integer division!
value = value / 10;
}
if (minus) {
result = "-" + result;
}
}
It looses 0's here:
while (valeur > 9)
{
valeur = valeur / 10;
y++;
}
You keep dividing the number until its less than 9, incrementing the count using the variable y.
Then you cant get it back using this:
value = (int)(value - (valeur * Math.Pow(10, y)));
I dont know what the point of this function is, I'm guessing ToString without using the inbuilt function, if so here is one way using a StringBuilder and Int Array:
private string convertToString(int value)
{
int[] valueAsArray = digitArr(value);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < valueAsArray.Length; i++)
{
sb.Append(valueAsArray[i]);
}
return sb.ToString();
}
public static int[] digitArr(int n)
{
if (n == 0) return new int[1] { 0 };
var digits = new List<int>();
for (; n != 0; n /= 10)
{
digits.Add(n%10);
}
var arr = digits.ToArray();
Array.Reverse(arr);
return arr;
}
I would urge you to fix your own function that way when your teacher checks here he see's you put in effort and worked it out using the debugger as phong mentioned...
This is a good example of when you have one known good function, and you want to test your own implementation against it. How might this code:
for (int i = 0; i < 100000; i++)
{
if (i.ToString() != ConvertToString(i))
{
Console.WriteLine(i);
ConvertToString(i);
}
}
help you? First of all, it will tell you which numbers your method doesn't work for. Second, you can attach the debugger to the WriteLine line, and then step through the second run through of your method.
Here's an example that works using Math.Log10 and Math.Pow. Step through it with the debugger to see why it doesn't drop any zeros (it did at first, but it was obvious why after using the seeing which cases is failed for, then using the debugger).
static string ConvertToString(int value)
{
string result = "";
int power = (int)Math.Log10(value);
while (power >= 0)
{
int nextLowestPowerOfTen = (int)Math.Pow(10, power);
int lowestMultipleOfPowerOfTen = (int)(value / nextLowestPowerOfTen);
result += lowestMultipleOfPowerOfTen;
value -= lowestMultipleOfPowerOfTen * nextLowestPowerOfTen;
power--;
}
return result;
}