I have an implementation of BFS that works just fine, but seems to get really CPU heavy, even at low depth (sub ms for a depth of 4 but 10 ms for a depth of 10). I'm quite confident that this algorithm should run sub ms event at a depth of 100 but i'm not quite sure what I'm missing.
Here's the code :
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class VisionGraph : MonoBehaviour
{
public Transform Ground;
public int Height;
public int Width;
private MeshFilter mf;
public Vector3[] Vertices;
public float precision;
public Vector3 SelectedVertex;
// Start is called before the first frame update
private void Start()
{
mf = Ground.GetComponent<MeshFilter>();
Matrix4x4 localToWorld = transform.localToWorldMatrix;
Vector3 world_max = localToWorld.MultiplyPoint3x4(mf.mesh.vertices[0]);
Width = Mathf.RoundToInt(world_max.z * (1 / precision));
int maxIndex = Mathf.RoundToInt((world_max.z * (1 / precision)) * (Height * (1 / precision)) * world_max.x);
Vertices = new Vector3[maxIndex];
//This is the graph initialization.
//Indices increment by 1 while actual coordinates increments by `precision`.
//Indices are then reversed to coordinates in BFS with `position/precision`.
int xInd, yInd, zInd; xInd = yInd = zInd = 0;
float x, y, z; x = y = z = 0;
int index = 0;
while (index < Vertices.Length - 1)
{
index = (yInd * (Width * Width)) + (zInd * Width) + xInd;
Debug.Log(index + " " + maxIndex);
Vertices[index] = new(x, y, z);
x += precision;
xInd++;
if (x > world_max.x)
{
x = 0;
xInd = 0;
z += precision;
zInd++;
if (z > world_max.z)
{
z = 0;
zInd = 0;
y += precision;
yInd++;
}
}
}
SelectedVertex = Vertices[600];
}
private void OnDrawGizmos()
{
// Needs to be turned into retrieve index from position.
// but i'm not sure how to clamp the continuous position to `precision` steps.
SelectedVertex = Vertices.Where(v => Vector3.Distance(v, SelectedVertex) <= precision - 0.0001 ).FirstOrDefault();
var watch = System.Diagnostics.Stopwatch.StartNew();
List<Vector3Int> closeVertices = BFS(SelectedVertex, 10); // second param is the search depth
watch.Stop();
Debug.Log(watch.ElapsedMilliseconds);
foreach (var vert in closeVertices)
{
var index = (vert.y * (Width * Width)) + (vert.z * Width) + vert.x;
if (index >= Vertices.Length) continue;
Gizmos.color = Color.red;
Gizmos.DrawSphere(Vertices[index], 0.1f);
}
}
private List<Vector3Int> BFS(Vector3 start, int depth)
{
Vector3Int startIndex = new((int)(start.x / precision), (int)(start.y / precision), (int)(start.z / precision));
Dictionary<Vector3Int, bool> closedList = new();
List<Vector3Int> queue = new() { startIndex };
while (queue.Count > 0)
{
Vector3Int v = queue[^1];
queue.RemoveAt(queue.Count-1);
Vector3Int[] neighbors = new[]
{
v + Vector3Int.left,
v + Vector3Int.right,
v + Vector3Int.up,
v + Vector3Int.down,
v + Vector3Int.forward,
v + Vector3Int.back,
};
foreach (Vector3Int n in neighbors)
{
if (n.x < 0 || n.y < 0 || n.z < 0) continue; // this will alos include the high limit of the grid but i dont "need" it at this point of the tests
//For every implementation of graph search algorithms I make, this always seem to bee the weak part.
if ((n - startIndex).sqrMagnitude > depth*depth || queue.Any(vert => vert == n) || closedList.ContainsKey(n)) continue;
queue.Insert(0, n);
}
closedList.Add(v, true);
}
return closedList.Keys.ToList();
}
}
The use of a Dictionary for the closedList is a poor attempt at trying to reduce the List searching time, it used to be closedList.Any(vert => vert == n) but I didn't see great change by the use of each.
I'd be really glad if somebody could pinpoint what really slow this down.
Second question : How do you even MultiThread with BFS ? Both the queue and the closed list are very dynamic, is there a solution to work this out with NativeLists ?
Thank you for your time. Let me know if anything's unclear.
Upfront, this may, or may not be suitable in your situation, but as an exercise for myself as much as anything, I thought it'd be interesting to see how fast I could get the execution of the given code.
I created three methods in my test.
The first was your method verbatim.
The second method was your code, but 'rejigged' but taking the
neighbours array creation out of the loop and adding in a
Queue<Vector3Int> instead of the List<Vector3Int> as I suspected
that the insertion to element 0 of the List<T> might be a big part of the issue in your original code also replacing your Dictionary<Vector3Int, bool> with a HashSet<Vector3Int>. I also replaced Any(v => v == n) with Contains(v). Contains turned out to be significantly faster.
Then the third method I created, I went even further to add a second HashSet to enable a fast lookup of items that were still in the queue.
I then ran the tests against each method using a StopWatch to record the times. I also ran the last method again, but that time from Task.Run.
Here are the two 'optimised' methods:
private List<Vector3Int> BFS_Optimised ( Vector3 start, int depth )
{
Vector3Int startIndex = new((int)(start.x / precision), (int)(start.y / precision), (int)(start.z / precision));
HashSet<Vector3Int> closedList = new ();
Queue<Vector3Int> queue = new ();
queue.Enqueue(startIndex);
var dSquared = depth * depth;
Vector3Int[] neighbors =
new[] { Vector3Int.left, Vector3Int.right, Vector3Int.up, Vector3Int.down, Vector3Int.forward, Vector3Int.back };
while ( queue.Count > 0 )
{
var v = queue.Dequeue();
for ( int i = 0; i < 6; ++i )
{
var n = v + neighbors[i];
if ( n.x < 0 || n.y < 0 || n.z < 0 )
continue; // this will alos include the high limit of the grid but i dont "need" it at this point of the tests
if ( ( n - startIndex ).sqrMagnitude > dSquared
|| closedList.Contains ( n )
|| queue.Contains ( n ) ) // queue.Any(v => v == n ) ) //
continue;
queue.Enqueue ( n );
}
closedList.Add ( v );
}
return closedList.ToList ( );
}
private List<Vector3Int> BFS_Optimised2 ( Vector3 start, int depth )
{
Vector3Int startIndex = new((int)(start.x / precision), (int)(start.y / precision), (int)(start.z / precision));
HashSet<Vector3Int> closedList = new ();
Queue<Vector3Int> queue = new ();
queue.Enqueue ( startIndex );
HashSet<Vector3Int> qHash = new ( ) { startIndex };
var dSquared = depth * depth;
Vector3Int[] neighbors =
new[] { Vector3Int.left, Vector3Int.right, Vector3Int.up, Vector3Int.down, Vector3Int.forward, Vector3Int.back };
while ( queue.Count > 0 )
{
var v = queue.Dequeue();
qHash.Remove ( v );
for ( int i = 0; i < 6; i++ )
{
var n = v + neighbors[i];
if ( n.x < 0 || n.y < 0 || n.z < 0 )
continue; // this will alos include the high limit of the grid but i dont "need" it at this point of the tests
if ( ( n - startIndex ).sqrMagnitude > dSquared
|| closedList.Contains ( n )
|| qHash.Contains ( n ) )
continue;
queue.Enqueue ( n );
qHash.Add ( n );
}
closedList.Add ( v );
}
return closedList.ToList ( );
}
Here's the test (excuse the crudeness and lack of depth in tests):
async void Run ( )
{
var iterations = 100;
var d = 10;
var v = new Vector3 ( 10, 10, 10 );
List<Vector3Int> r1 = default;
List<Vector3Int> r2 = default;
List<Vector3Int> r3 = default;
List<Vector3Int> r4 = default;
Debug.Log ( "Waiting ... " );
await Task.Delay ( 2000 );
Debug.Log ( "Run ... " );
Stopwatch sw = new();
sw.Start ( );
for ( int i = 0; i < iterations; i++ )
r1 = BFS ( v, d );
sw.Stop ( );
var t1 = sw.Elapsed.TotalMilliseconds;
sw.Restart ( );
for ( int i = 0; i < iterations; i++ )
r2 = BFS_Optimised ( v, d );
sw.Stop ( );
var t2 = sw.Elapsed.TotalMilliseconds;
sw.Restart ( );
for ( int i = 0; i < iterations; i++ )
r3 = BFS_Optimised2 ( v, d );
sw.Stop ( );
var t3 = sw.Elapsed.TotalMilliseconds;
sw.Restart ( );
r4 = await Task.Run ( ( ) => BFS_Optimised2 ( v, d ) );
sw.Stop ( );
var t4 = sw.Elapsed.TotalMilliseconds;
StringBuilder sb = new();
sb.AppendLine ( $"Original : {t1} ms [{r1.Count}] ({r1 [ 0 ]}) .. ({r1 [ ^1 ]})" );
sb.AppendLine ( $"Optimised : {t2} ms [{r2.Count}] ({r2 [ 0 ]}) .. ({r2 [ ^1 ]})" );
sb.AppendLine ( $"Optimised2 : {t3} ms [{r3.Count}] ({r3 [ 0 ]}) .. ({r3 [ ^1 ]})" );
sb.AppendLine ( $"Optimised2 Task.Run : {t4} ms [{r4.Count}] ({r4 [ 0 ]}) .. ({r4 [ ^1 ]})" );
Debug.Log ( sb.ToString ( ) );
}
And here are the results:
Original : 10701.7465 ms [4169] ((10, 10, 10)) .. ((15, 5, 3))
Optimised : 1830.9519 ms [4169] ((10, 10, 10)) .. ((15, 5, 3))
Optimised2 : 209.1559 ms [4169] ((10, 10, 10)) .. ((15, 5, 3))
Optimised2 Task.Run : 17.7353 ms [4169] ((10, 10, 10)) .. ((15, 5, 3))
I cannot make out at a glance what your code is doing ( there is no docs and my C# is rusty ) But your BFS has an surprising amount of code ( A BFS usually only takes a few lines ). Why do you need to create a new, large data structure on EVERY pass through the while loop? That is expensive, and as far as I recall C# uses some bedeviled garbage collector thingy that is going to be fighting you all the way
You have a variable named queue, but it is not a queue but rather a list. This is confusing, and probably incorrect.
As far as I can make out, you are creating the neighbors of a node on the fly as you go along. Terrible idea! You should model the graph either with an adjacency matrix ( simplest ) or node adjacency lists ( more complicated, but memory efficient for large graphs ) Then you can move through the graph using the node indices to look up the neighbours without needing to create them over and over again.
For comparison, here is a C++ code for a BFS that uses a real queue and concerns itself only with node indices.
void cPathFinder::breadth(
std::function<void(int v, int p)> visitor)
{
if (!nodeCount())
throw std::runtime_error("breadth called on empty graph");
std::vector<bool> visited(nodeCount(), false);
std::queue<int> Q;
visited[myStart] = true;
Q.push(myStart);
while (Q.size())
{
int v = Q.front();
Q.pop();
for (int w : adjacent(v))
{
if (!visited[w])
{
// reached a new node
visitor(w, v);
visited[w] = true;
Q.push(w);
}
}
}
}
Related
I have a game code,is working and doing:
*Spawn one animal in a floor tile when tile is accepted,but doing in all accepted tiles.
I want the script do it just one time,here the code:
public static void Generate( Rectangle2D region )
{
int OakTree = 0x12B9;
for ( int rx = 0; rx < region.Width; ++rx )
{
for ( int ry = 0; ry < region.Height; ++ry )
{
int vx = rx + region.X;
int vy = ry + region.Y;
StaticTile[] tiles = m_Map.Tiles.GetStaticTiles( vx, vy );
for ( int i = 0; i < tiles.Length; ++i )
{
StaticTile tile = tiles[i];
int id = tile.ID;
id &= 0x3FFF;
int z = tile.Z;
if ( IsRock( id ) )
{
AddAnimalSpawner( vx + 1, vy, z );
}
}
}
}
}
What is the correct way to somply it to just one time?I think the problem is for ( int i = 0; i < tiles.Length; ++i ) .
Thank you!
If I understood you correctly you want to call AddAnimalSpawner just one time.
You can simply use the break keyword. (https://learn.microsoft.com/de-de/dotnet/csharp/language-reference/keywords/break) to return to the outer loops or return to exit the Generate(Rectangle2D region) Function.
Example:
for ( int i = 0; i < tiles.Length; ++i )
{
StaticTile tile = tiles[i];
int id = tile.ID;
id &= 0x3FFF;
int z = tile.Z;
if ( IsRock( id ) )
{
AddAnimalSpawner( vx + 1, vy, z );
break;
}
}
or
for ( int i = 0; i < tiles.Length; ++i )
{
StaticTile tile = tiles[i];
int id = tile.ID;
id &= 0x3FFF;
int z = tile.Z;
if ( IsRock( id ) )
{
AddAnimalSpawner( vx + 1, vy, z );
return;
}
}
As you are already mentioning: you are getting a collection of tiles (by calling GetStaticTiles), and then you add an animal to all of them after verifying that the tile is rock (I guess this has to do with other kind of tiles, like water, where the animal cannot be placed, right?).
In order to do this only one time, you have to break the for look after adding the animal the first time. Here you have several options. One of them is to rewrite your code to better describe what you are doing, but the simpler option you have may be to simply set
i = tiles.Length;
after calling AddAnimalSpawner. This will end the for loop in the next iteration (i.e.: almost immediately) after placing only one animal.
Note that by following the latter method, the outer loop (the one iterating over the regions) will still run. If you don't want that to happen, then you can simply return from the function after adding the only animal.
One of the requirements for Telegram Authentication is decomposing a given number into 2 prime co-factors. In particular P*Q = N, where N < 2^63
How can we find the smaller prime co-factor, such that P < square_root(N)
My Suggestions:
1) pre-compute primes from 3 to 2^31.5, then test if N mod P = 0
2) Find an algorithm to test for primes (but we still have to test N mod P =0)
Is there an algorithm for primes that is well suited to this case?
Pollard's Rho Algorithm [VB.Net]
Finds P very fast, where P*Q = N, for N < 2^63
Dim rnd As New System.Random
Function PollardRho(n As BigInteger) As BigInteger
If n Mod 2 = 0 Then Return 2
Dim x As BigInteger = rnd.Next(1, 1000)
Dim c As BigInteger = rnd.Next(1, 1000)
Dim g As BigInteger = 1
Dim y = x
While g = 1
x = ((x * x) Mod n + c) Mod n
y = ((y * y) Mod n + c) Mod n
y = ((y * y) Mod n + c) Mod n
g = gcd(BigInteger.Abs(x - y), n)
End While
Return g
End Function
Function gcd(a As BigInteger, b As BigInteger) As BigInteger
Dim r As BigInteger
While b <> 0
r = a Mod b
a = b
b = r
End While
Return a
End Function
Richard Brent's Algorithm [VB.Net] This is even faster.
Function Brent(n As BigInteger) As BigInteger
If n Mod 2 = 0 Then Return 2
Dim y As BigInteger = rnd.Next(1, 1000)
Dim c As BigInteger = rnd.Next(1, 1000)
Dim m As BigInteger = rnd.Next(1, 1000)
Dim g As BigInteger = 1
Dim r As BigInteger = 1
Dim q As BigInteger = 1
Dim x As BigInteger = 0
Dim ys As BigInteger = 0
While g = 1
x = y
For i = 1 To r
y = ((y * y) Mod n + c) Mod n
Next
Dim k = New BigInteger(0)
While (k < r And g = 1)
ys = y
For i = 1 To BigInteger.Min(m, r - k)
y = ((y * y) Mod n + c) Mod n
q = q * (BigInteger.Abs(x - y)) Mod n
Next
g = gcd(q, n)
k = k + m
End While
r = r * 2
End While
If g = n Then
While True
ys = ((ys * ys) Mod n + c) Mod n
g = gcd(BigInteger.Abs(x - ys), n)
If g > 1 Then
Exit While
End If
End While
End If
Return g
End Function
Ugh! I just put this program in and then realized you had tagged your question C#. This is C++, a version of Pollard Rho I wrote a couple years ago and posted here on SO to help someone else understand it. It is many times faster at factoring semiprimes than trial division is. As I said, I regret that it is C++ and not C#, but you should be able to understand the concept and even port it pretty easily. As a bonus, the .NET library has a namespace for handling arbitrarily large integers where my C++ implementation required me to go find a third party library for them. Anyway, even in C#, the below program will break a 2^63 order semiprime into 2 primes in less than 1 second. There are faster algorithms even than this, but they are much more complex.
#include <string>
#include <stdio.h>
#include <iostream>
#include "BigIntegerLibrary.hh"
typedef BigInteger BI;
typedef BigUnsigned BU;
using std::string;
using std::cin;
using std::cout;
BU pollard(BU &numberToFactor);
BU gcda(BU differenceBetweenCongruentFunctions, BU numberToFactor);
BU f(BU &x, BU &numberToFactor, int &increment);
void initializeArrays();
BU getNumberToFactor ();
void factorComposites();
bool testForComposite (BU &num);
BU primeFactors[1000];
BU compositeFactors[1000];
BU tempFactors [1000];
int primeIndex;
int compositeIndex;
int tempIndex;
int numberOfCompositeFactors;
bool allJTestsShowComposite;
int main ()
{
while(1)
{
primeIndex=0;
compositeIndex=0;
tempIndex=0;
initializeArrays();
compositeFactors[0] = getNumberToFactor();
cout<<"\n\n";
if (compositeFactors[0] == 0) return 0;
numberOfCompositeFactors = 1;
factorComposites();
}
}
void initializeArrays()
{
for (int i = 0; i<1000;i++)
{
primeFactors[i] = 0;
compositeFactors[i]=0;
tempFactors[i]=0;
}
}
BU getNumberToFactor ()
{
std::string s;
std::cout<<"Enter the number for which you want a prime factor, or 0 to quit: ";
std::cin>>s;
return stringToBigUnsigned(s);
}
void factorComposites()
{
while (numberOfCompositeFactors!=0)
{
compositeIndex = 0;
tempIndex = 0;
// This while loop finds non-zero values in compositeFactors.
// If they are composite, it factors them and puts one factor in tempFactors,
// then divides the element in compositeFactors by the same amount.
// If the element is prime, it moves it into tempFactors (zeros the element in compositeFactors)
while (compositeIndex < 1000)
{
if(compositeFactors[compositeIndex] == 0)
{
compositeIndex++;
continue;
}
if(testForComposite(compositeFactors[compositeIndex]) == false)
{
tempFactors[tempIndex] = compositeFactors[compositeIndex];
compositeFactors[compositeIndex] = 0;
tempIndex++;
compositeIndex++;
}
else
{
tempFactors[tempIndex] = pollard (compositeFactors[compositeIndex]);
compositeFactors[compositeIndex] /= tempFactors[tempIndex];
tempIndex++;
compositeIndex++;
}
}
compositeIndex = 0;
// This while loop moves all remaining non-zero values from compositeFactors into tempFactors
// When it is done, compositeFactors should be all 0 value elements
while (compositeIndex < 1000)
{
if (compositeFactors[compositeIndex] != 0)
{
tempFactors[tempIndex] = compositeFactors[compositeIndex];
compositeFactors[compositeIndex] = 0;
tempIndex++;
compositeIndex++;
}
else compositeIndex++;
}
compositeIndex = 0;
tempIndex = 0;
// This while loop checks all non-zero elements in tempIndex.
// Those that are prime are shown on screen and moved to primeFactors
// Those that are composite are moved to compositeFactors
// When this is done, all elements in tempFactors should be 0
while (tempIndex<1000)
{
if(tempFactors[tempIndex] == 0)
{
tempIndex++;
continue;
}
if(testForComposite(tempFactors[tempIndex]) == false)
{
primeFactors[primeIndex] = tempFactors[tempIndex];
cout<<primeFactors[primeIndex]<<"\n";
tempFactors[tempIndex]=0;
primeIndex++;
tempIndex++;
}
else
{
compositeFactors[compositeIndex] = tempFactors[tempIndex];
tempFactors[tempIndex]=0;
compositeIndex++;
tempIndex++;
}
}
compositeIndex=0;
numberOfCompositeFactors=0;
// This while loop just checks to be sure there are still one or more composite factors.
// As long as there are, the outer while loop will repeat
while(compositeIndex<1000)
{
if(compositeFactors[compositeIndex]!=0) numberOfCompositeFactors++;
compositeIndex ++;
}
}
return;
}
// The following method uses the Miller-Rabin primality test to prove with 100% confidence a given number is composite,
// or to establish with a high level of confidence -- but not 100% -- that it is prime
bool testForComposite (BU &num)
{
BU confidenceFactor = 101;
if (confidenceFactor >= num) confidenceFactor = num-1;
BU a,d,s, nMinusOne;
nMinusOne=num-1;
d=nMinusOne;
s=0;
while(modexp(d,1,2)==0)
{
d /= 2;
s++;
}
allJTestsShowComposite = true; // assume composite here until we can prove otherwise
for (BI i = 2 ; i<=confidenceFactor;i++)
{
if (modexp(i,d,num) == 1)
continue; // if this modulus is 1, then we cannot prove that num is composite with this value of i, so continue
if (modexp(i,d,num) == nMinusOne)
{
allJTestsShowComposite = false;
continue;
}
BU exponent(1);
for (BU j(0); j.toInt()<=s.toInt()-1;j++)
{
exponent *= 2;
if (modexp(i,exponent*d,num) == nMinusOne)
{
// if the modulus is not right for even a single j, then break and increment i.
allJTestsShowComposite = false;
continue;
}
}
if (allJTestsShowComposite == true) return true; // proven composite with 100% certainty, no need to continue testing
}
return false;
/* not proven composite in any test, so assume prime with a possibility of error =
(1/4)^(number of different values of i tested). This will be equal to the value of the
confidenceFactor variable, and the "witnesses" to the primality of the number being tested will be all integers from
2 through the value of confidenceFactor.
Note that this makes this primality test cryptographically less secure than it could be. It is theoretically possible,
if difficult, for a malicious party to pass a known composite number for which all of the lowest n integers fail to
detect that it is composite. A safer way is to generate random integers in the outer "for" loop and use those in place of
the variable i. Better still if those random numbers are checked to ensure no duplicates are generated.
*/
}
BU pollard(BU &n)
{
if (n == 4) return 2;
BU x = 2;
BU y = 2;
BU d = 1;
int increment = 1;
while(d==1||d==n||d==0)
{
x = f(x,n, increment);
y = f(y,n, increment);
y = f(y,n, increment);
if (y>x)
{
d = gcda(y-x, n);
}
else
{
d = gcda(x-y, n);
}
if (d==0)
{
x = 2;
y = 2;
d = 1;
increment++; // This changes the pseudorandom function we use to increment x and y
}
}
return d;
}
BU gcda(BU a, BU b)
{
if (a==b||a==0)
return 0; // If x==y or if the absolute value of (x-y) == the number to be factored, then we have failed to find
// a factor. I think this is not proof of primality, so the process could be repeated with a new function.
// For example, by replacing x*x+1 with x*x+2, and so on. If many such functions fail, primality is likely.
BU currentGCD = 1;
while (currentGCD!=0) // This while loop is based on Euclid's algorithm
{
currentGCD = b % a;
b=a;
a=currentGCD;
}
return b;
}
BU f(BU &x, BU &n, int &increment)
{
return (x * x + increment) % n;
}
Requirement: Iterate through a sorted list of strings, adding a char at the beginning of each string, then re-sorting. This may need to be done a few thousand times. I tried using a regular List of strings but, as expected, the process was way too slow.
I was going to try a List of StringBuilders but there is no direct way to sort the list. Any workarounds come to mind?
You've stated you can't sort a Link - however, you can if you can supply your own sort comparison:
List<StringBuilder> strings = new List<StringBuilder>();
// ...
strings.Sort((s1, s2) => s1.ToString().CompareTo(s2.ToString()));
The problem here as #phoog notes, is that in order to do so it allocates a lot of extra strings and isn't very efficient. The sort he provides is better. What we can do to figure out which approach is better is supply a test. You can see the fiddle here: http://dotnetfiddle.net/Px4fys
The fiddle uses very few strings and very few iterations because it's in a fiddle and there's a memory limit. If you paste this into a console app and run in Release you'll find there's huge differences. As #phoog also suggests LinkedList<char> wins hands-down. StringBuilder is the slowest.
If we bump up the values and run in Release mode:
const int NumStrings= 1000;
const int NumIterations= 1500;
We'll find the results:
List<StringBuilder> - Elapsed Milliseconds: 27,678
List<string> - Elapsed Milliseconds: 2,932
LinkedList<char> - Elapsed Milliseconds: 912
EDIT 2: When I bumped both values up to 3000 and 3000
List<StringBuilder> - Elapsed Milliseconds: // Had to comment out - was taking several minutes
List<string> - Elapsed Milliseconds: 45,928
LinkedList<char> - Elapsed Milliseconds: 6,823
The string builders will be a bit quicker than strings, but still slow, since you have to copy the entire buffer to add a character at the beginning.
You can create a custom comparison method (or comparer object if you prefer) and pass it to the List.Sort method:
int CompareStringBuilders(StringBuilder a, StringBuilder b)
{
for (int i = 0; i < a.Length && i < b.Length; i++)
{
var comparison = a[i].CompareTo(b[i]);
if (comparison != 0)
return comparison;
}
return a.Length.CompareTo(b.Length);
}
Invoke it like this:
var list = new List<StringBuilder>();
//...
list.Sort(CompareStringBuilders);
You would probably do better to look for a different solution to your problem, however.
Linked lists offer quick prepending, so how about using LinkedList<char>? This might not work if you need other StringBuilder functions, of course.
StringBuilder was rewritten for .NET 4, so I've struck out my earlier comments about slow prepending of characters. If performance is an issue, you should test to see where the problems actually lie.
Thanks to all for the suggestions posted. I checked these, and I have to say that I'm astonished that LinkedList works incredibly well, except for memory usage.
Another surprise is the slow sorting speed of the StringBuilder list. It works quickly as expected for the char insert phase. But the posted benchmarks above reflect what I've found: StringBuilder sorts very slowly for some reason. Painfully slow.
List of strings sorts faster. But counter to intuition, List of LinkedList sorts very fast. I have no idea how navigating a linked list could possibly be faster than simple indexing of a buffer (as in strings and StringBuilder), but it is. I would never have thought to try LinkedList. Compliments to McAden for the insight!
But unfortunately, LinkedList runs the system out of RAM. So, back to the drawing board.
Sort the StringBuilders as described in Phoog's answer, but keep the strings in reverse order in the StringBuilder instances - this way, you can optimize the "prepending" of each new character by appending it to the end of the StringBuilder's current value:
Update: with test program
class Program
{
static readonly Random _rng = new Random();
static void Main(string[] args)
{
int stringCount = 2500;
int initialStringSize = 100;
int maxRng = 4;
int numberOfPrepends = 2500;
int iterations = 5;
Console.WriteLine( "String Count: {0}; # of Prepends: {1}; # of Unique Chars: {2}", stringCount, numberOfPrepends, maxRng );
var startingStrings = new List<string>();
for( int i = 0; i < stringCount; ++i )
{
var sb = new StringBuilder( initialStringSize );
for( int j = 0; j < initialStringSize; ++j )
{
sb.Append( _rng.Next( 0, maxRng ) );
}
startingStrings.Add( sb.ToString() );
}
for( int i = 0; i < iterations; ++i )
{
TestUsingStringBuilderAppendWithReversedStrings( startingStrings, maxRng, numberOfPrepends );
TestUsingStringBuilderPrepend( startingStrings, maxRng, numberOfPrepends );
}
var input = Console.ReadLine();
}
private static void TestUsingStringBuilderAppendWithReversedStrings( IEnumerable<string> startingStrings, int maxRng, int numberOfPrepends )
{
var builders = new List<StringBuilder>();
var start = DateTime.Now;
foreach( var str in startingStrings )
{
builders.Add( new StringBuilder( str ).Reverse() );
}
for( int i = 0; i < numberOfPrepends; ++i )
{
foreach( var sb in builders )
{
sb.Append( _rng.Next( 0, maxRng ) );
}
builders.Sort( ( x, y ) =>
{
var comparison = 0;
var xOffset = x.Length;
var yOffset = y.Length;
while( 0 < xOffset && 0 < yOffset && 0 == comparison )
{
--xOffset;
--yOffset;
comparison = x[ xOffset ].CompareTo( y[ yOffset ] );
}
if( 0 != comparison )
{
return comparison;
}
return xOffset.CompareTo( yOffset );
} );
}
builders.ForEach( sb => sb.Reverse() );
var end = DateTime.Now;
Console.WriteLine( "StringBuilder Reverse Append - Total Milliseconds: {0}", end.Subtract( start ).TotalMilliseconds );
}
private static void TestUsingStringBuilderPrepend( IEnumerable<string> startingStrings, int maxRng, int numberOfPrepends )
{
var builders = new List<StringBuilder>();
var start = DateTime.Now;
foreach( var str in startingStrings )
{
builders.Add( new StringBuilder( str ) );
}
for( int i = 0; i < numberOfPrepends; ++i )
{
foreach( var sb in builders )
{
sb.Insert( 0, _rng.Next( 0, maxRng ) );
}
builders.Sort( ( x, y ) =>
{
var comparison = 0;
for( int offset = 0; offset < x.Length && offset < y.Length && 0 == comparison; ++offset )
{
comparison = x[ offset ].CompareTo( y[ offset ] );
}
if( 0 != comparison )
{
return comparison;
}
return x.Length.CompareTo( y.Length );
} );
}
var end = DateTime.Now;
Console.WriteLine( "StringBulder Prepend - Total Milliseconds: {0}", end.Subtract( start ).TotalMilliseconds );
}
}
public static class Extensions
{
public static StringBuilder Reverse( this StringBuilder stringBuilder )
{
var endOffset = stringBuilder.Length - 1;
char a;
for( int beginOffset = 0; beginOffset < endOffset; ++beginOffset, --endOffset )
{
a = stringBuilder[ beginOffset ];
stringBuilder[ beginOffset ] = stringBuilder[ endOffset ];
stringBuilder[ endOffset ] = a;
}
return stringBuilder;
}
}
results:
2500 strings initially at 100 characters, 2500 prepends:
.net 4
vs2010
winform
c#
added some points using
chart1.Series[0].Points.AddXY(x,y);
when I click on the chart, the cursor may not fall on any points.
Are there any functions to return the nearest point? (forget y, just x distance.)
Or I have to write my own binary search function?
private void Chart_MouseClick(object sender, MouseButtonEventArgs e)
{
LineSeries line = (LineSeries)mychart.Series[0];
Point point = e.GetPosition(line);
Int32? selectIndex = FindNearestPointIndex(line.Points, point);
// ...
}
private Int32? FindNearestPointIndex(PointCollection points, Point point)
{
if ((points == null || (points.Count == 0))
return null;
Func<Point, Point, Double> getLength = (p1, p2) => Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2)); // C^2 = A^2 + B^2
List<Points> results = points.Select((p,i) => new { Point = p, Length = getLength(p, point), Index = i }).ToList();
Int32 minLength = results.Min(i => i.Length);
return results.First(i => (i.Length == minLength)).Index;
}
To find the nearest point in a set of unordered points, you have to iterate through them all and keep track of the minimum distance. This has a time complexity of O(n).
You could significantly improve this by maintaining the points in a more organized data structure (such as an R-tree). There are third-party libraries available if you'd rather not implement your own. Many databases already support the R-tree for spatial indices.
If you really want to only search for the point with the nearest X-coordinate, this could be further simplified by storing the points in a sorted collection (such as a SortedList<TKey, TValue>) and performing a binary search (which SortedList<TKey, TValue>.IndexOfKey already implements).
/*My Fuzzy Binary Search*/
private int FindNearestId(System.Windows.Forms.DataVisualization.Charting.DataPointCollection p, uint ClickedX)
{
int ret = 0;
int low = 0;
int high = p.Count - 1;
bool bLoop = true;
while (bLoop)
{
ret = (low + high) / 2;
switch (FindNearestId_Match(p, ClickedX, ret))
{
case 0:
high = ret+1;
break;
case 1:
bLoop = false;
break;
case 2:
low = ret-1;
break;
}
}
return ret+1;
}
private int FindNearestId_Match(System.Windows.Forms.DataVisualization.Charting.DataPointCollection p, uint ClickedX, int id)
{
uint id0 = Convert.ToUInt32(p[id].XValue);
uint id1 = Convert.ToUInt32(p[id+1].XValue);
if ( (id0 <= ClickedX) && (ClickedX < id1) )
{
return 1;
}
else if ((id0 < ClickedX) && (ClickedX > id1))
{
return 2;
}
else
{
return 0;
}
}
Soultion can be more clear.
( as above you should use log complexity for accessing item )
double x-values solution:
double FindNearestPointYValueInSeries( System::Windows::Forms::DataVisualization::Charting::Series ^pxSeries, double dSearchedPosition )
{
int i_min = 0;
int i_max = pxSeries->Points->Count - 1;
int i_mean = 0;
double d ;
if ( i_max < 0 ) // not defined - minimum one point required
return Double::NaN;
while ( i_min <= i_max )
{
i_mean = (i_max + i_min ) / 2; // index of compared value in series
d = pxSeries->Points[ i_mean ]->XValue; // compared value
if ( d > dSearchedPosition ) // greater - search in right part
i_max = i_mean - 1;
else if ( d < dSearchedPosition ) // lower - search in left part
i_min = i_mean + 1;
else // equal ?
return d;
}
// delta is dSearchedPosition - pxSeries->Points[ i_mean ]->YValues[0]
// get Y value ( on index 0 )
return pxSeries->Points[ i_mean ]->YValues[0];
}
At the very outset let me express my sincere thanks to Marc Gravel,Dahlbyk and the rest for helping me to apply linq practically.
The following are few questions which I have faced in an interview to solve applying Linq. As I am not familiar with Linq I solved it without using Linq.
I appreciate the answers which helps me to solve them using Linq
Thanks in advance.
Question 1:
The Problem is to find different digits such that,in whatever order they are used to make a three-digit number,that number will not be divisible by:
3,5,7,11,13 or 17.
To ensure that there is no ambuigity,suppose the three digits
are a,b,and c.Then,none of the combination of the numbers:
say abc,acb,bac,bca,cab and cba will divide by 3,5,7,11,13 or 17.
Example :
When I take 248 none of its combination(284,428,482,842,824) will exactly divisible by 3,5,7,11,13 or 17.
public void FindingRareNumbers()
{
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= 9; j++)
{
for (int k = 1; k <= 9; k++)
{
//to form the three digit
string digit = i.ToString() + j.ToString() + k.ToString();
//converting to integer
int StrToDigit = Convert.ToInt32(digit);
char[] digitcombination = digit.ToCharArray();
string PossibleCombination = "";
bool testpassed = false;
int dcount = 0;
#region different possible combinations
for (int p = 0; p <= 2; p++)
{
for (int q = 0; q <= 2; q++)
{
for (int r = 0; r <= 2; r++)
{
// The following condition avoid the repeatance
// of digit like 111,111,111
if (p != q && p != r && r != q)
{
PossibleCombination =
digitcombination[p].ToString() +
digitcombination[q].ToString() +
digitcombination[r].ToString();
int num = Convert.ToInt32(PossibleCombination);
if (num % 3 != 0 && num % 5 != 0 && num % 7 != 0
&& num % 11 != 0 && num % 11 != 0
&& num % 13 != 0 && num % 17 != 0)
{
//count is increment for 6 times
// it satisfies the condition
dcount++;
testpassed = true;
}
}
}
}
}
#endregion combination
if (testpassed && dcount==6)
{
Console.WriteLine(StrToDigit);
}
}
}
}
}
(coding is working)
Question 2:
The task is to arrange the element in matrix so that all rows,columns,and diagonals add up to the same total.(Bit problem in coding ,I am trying to solve it).
------------------
1 2 3
-----------------
4 5 6
-----------------
7 8 9
-----------------
example :
The one of solutions is as follows:
-----------
2 9 4
-----------
7 5 3
----------
6 1 8
----------
I agree that Marc's solution to your first problem is a reasonable approach. But I think there's a larger question here, which is "how do I solve problems like this in a LINQ-ish manner?"
Notice how your solution is completely "procedural" and "imperative". Your code specifies a series of steps that you would execute, one after the other, with deep loops. Each step along the way is meaningless unless you understand its place in the larger whole.
There are two ideas I like to use when solving problems with LINQ:
Describe what the program is doing logically, rather than listing a series of commands
Characterize the problem as a query against a data set rather than as a procedure to follow.
So, what's our data set? We wish to filter out some elements from the set of all combinations of three digits.
How do we filter them? Permute the digits and then perform a divisibility check on each permutation.
OK, so now we have a structure for our program:
var query = from c in ThreeDigitCombinations()
where DivisibilityCheckPasses(c)
select c;
foreach(Combination result in query) Console.WriteLine(result);
And now you can continue breaking down each of those further, solving each sub-problem using LINQ in turn.
Same goes for your "magic square" problem; you're looking for a permutation that has a certain property, so write a generator of permutations, write a filter, and execute it.
For the first:
static IEnumerable<int> Permute(int x, int y, int z)
{
yield return x * 100 + y * 10 + z;
yield return x * 100 + z * 10 + y;
yield return y * 100 + x * 10 + z;
yield return y * 100 + z * 10 + x;
yield return z * 100 + x * 10 + y;
yield return z * 100 + y * 10 + x;
}
static void Main()
{
var divs = new[] {3,5,7,11,13,17};
// combinations of 1-9
var combinations =
from x in Enumerable.Range(1, 7)
from y in Enumerable.Range(x + 1, 8 - x)
from z in Enumerable.Range(y + 1, 9 - y)
select new { x, y, z };
// permute
var qry = from comb in combinations
where !Permute(comb.x, comb.y, comb.z).Any(
i => divs.Any(d => i % d == 0))
select comb;
foreach (var answer in qry)
{
Console.WriteLine("{0}, {1}, {2}", answer.x, answer.y, answer.z);
}
}
For the second - not elegant, but it works (returns the 8 permutations of the sample):
static void Main() {
var data = Enumerable.Range(1, 9);
var magicSquares =
// generate 1st row and deduce the target
from a in data let arrA = new[] { a }
from b in data.Except(arrA) let arrB = new[] { a,b }
from c in data.Except(arrB) let arrC = new[] { a,b,c }
let target = a + b + c
// generate 2nd row and filter to target matches
from d in data.Except(arrC) let arrD = new[] { a,b,c,d }
from e in data.Except(arrD) let arrE = new[] { a,b,c,d,e }
from f in data.Except(arrE) let arrF = new[] { a,b,c,d,e,f }
where d + e + f == target
// generate 3rd row and filter to target matches
from g in data.Except(arrF) let arrG = new[] { a,b,c,d,e,f,g }
from h in data.Except(arrG) let arrH = new[] { a,b,c,d,e,f,g,h }
from i in data.Except(arrH)
where g + h + i == target
// filter columns
&& a + d + g == target
&& b + e + h == target
&& c + f + i == target
// filter diagonals
&& a + e + i == target
&& c + e + g == target
select new {a,b,c,d,e,f,g,h,i};
foreach (var row in magicSquares)
{
Console.WriteLine("{0} {1} {2}", row.a, row.b, row.c);
Console.WriteLine("{0} {1} {2}", row.d, row.e, row.f);
Console.WriteLine("{0} {1} {2}", row.g, row.h, row.i);
Console.WriteLine();
}
}