I am trying to invent bycicle which should be already invented :)
so, I have sorted dictionary:
1:val1,
2:val3,
3:val3
now, I need insert new item between 1 - 2 key without moving the key(2 and 3). There is one way- insert key with 1.5 value but it is under question how many item can I insert between 1-2 and which algorithm is suitable to do so.
It is possible to insert N items between them......
the Data type or the format of the key dos not metter, just it crucial that key dos not change
You can use alphabet as keys. New key will be concatenated string of previous and next key.
For example:
If you have A and B, new key will be AB:
A
AB (A + B)
B
to add new key between A and AB, you will have AAB:
A
AAB (A + AB)
AB (A +B)
B
and so on...
You can use BigDecimal in Java to get new_key=(key1+key2)/2.
import java.math.BigDecimal;
public class CodeSheet {
public static void main(String[] args) {
BigDecimal a = new BigDecimal(1);
BigDecimal two = new BigDecimal(2);
for(int i = 0; i <= 6000;i ++)
{
a = a.divide(two);
if (i%1000 == 0)
System.out.println(a.toString());
}
}
}
output:
0.5
4.666318092516094394950447723619085848085457231858540123108571698979834554878878172272201635489405511797974949651621213121077437606770161974207604086019653781172053330691625751369975379929509157555502453981325565591202562573979668954025891355627075519053491894272132405597349071143304796110088314552213992280847244435737332640031641842263237146309149310826013965976447468035589253318343705327199027653590681602999224130209770506066148149347510972573049521073043341806223964760174134323088289634580237100329681945208689479110591825390227783142221369626937585639273983907781732018574388408834499276960346327197120043559868373508749313133453736483812679019646881169169905234639372793026268482208251953125E-302
4.354904908108608337788097747389436147929551871352694308321746614749144426703133706892369375399893940532782043588727560318102151185994168162541395412261518430550755321155148659223854561694954711924029281870986173595315518548585511706690334120707350364118146386757419736828045574038868295421914635929079059744555673086284578352082725382878484893441482333124196570054633787834328204149323035102391048682919453726835836055542393496585133335537308533180937973567503195969258360476033334393391821525284565813777886412721596101172364149931162673561491299813436308231347105575294992080347939004544602896748346183684569136737241491985864479016388335577898767470263560575437108148985779824881400524519876725232838928477267134127170926478227142121467310560266975687300142692457732481335134494432171071782408627253792130227205541778953790826318813538185488987468045762324286746160892737356344542977960827017103483581102720485435212671525873919071359975592227058010595009966338494815791830176096729490346274067433317969857326763618026109468018115811724815116133999445116665877671509729075069315638648723850028991409064960833766332205861977229046767218582849546541487471215035845260620260257995532242830003020146307439405502318054114708229860672592763223579453418947532879305088491927160146050190013971612697710103760111069333206902362883772710769598910416379683242622746115746412698399581131525337696075439453125E-603
4.064274312778867720235939028734255660766324543474839164530422188377250904692462466209591864260587971411331478774338224911275926886702626758550412028002712137706367368146396620253064136127073264858942335915257829998865098421249280979778656504235680311966272657056163696546167149941924660404935695895945028011534537971659758008693505558573200513078683510452651595620409830786639557494530833737093935568272875104139425826158032062394577918465219998245130052918160097042806762070602493653493626216406119810227553087853066722753388827490288177690729623901043570687373981862722140724176570320050741221327417827648494038121701089062505713056402546071605393776743633905210424237918734689348418886901559587016827788694266756655317144124682859190661867093972217932288628949680878455501011902943704213579623344964181809419242960540066091034125167128849103858978675507408455806146209191851472240678382070178087429856622047212175931655973014000019630325498473096560571602518169186419530087543188764835897219142880682252597989279812191578193716163187575327957591625965057446212814094560426730589262600928737872525933767331411105248595121827696798608491093243582843484129095100597421642936657760266153001856285613174837664729189258437310502932083077322244297462818854139350309998096123305359911095115904480646457999254743328163360465724236166676478809329603593817106724193744306683490394814672540103839616421520608733694040189710678874823730575477314252903579788549424755469905133470201451695223803806440596745401726146987923905610169014059776971596451074394178160713636961030621285663930888688208102523067395389210836279358605667255768920628104803766115059613394743085226143092748238730794960406636454230915469276113065925011064938129865019339567665464818230330165724148468941280304362503380341826473700203394927473837564110162168494638664204304239237821253725128209855798555318373561952586104070384276660875768023522310029611970728612015242875836349942659673401524737492334455205215164285934911013390410191470865217345765170604606902120008883954298607285769450192614645096800263246450413134880363941192626953125E-904
3.793039351733689286115602651843418575407592505563559767838544978475768217731986254376334108338556729108108742576832030998209717600316405171714055148655625096531883980134466070609936781559539370211169480252948213114277039837881063371677982969942447634651142331829192990506994554605009729046508341063337270064533739907321744792853183498549053351652988350920074096638285429862308391372954932006828359193622721419231928621489711340365241716248314514812885520219410680477334527856708284138839427973457868801553311949616569124359090488609460035208780642970555722141753015176081595007867635360093435776178190543350036841708399408539367940510076413956467316430916095391711744186998859008657799555644735253422641584786163850401369234332768914588781386187196795711351724957749391652361622962074629197788024298287754370852707402508636707853904761054362146259460871849920284565328911164746852351234728953672011980064743223911894197685446274274878390523329351901158826070800120347703561421615880371006639670348315766341149012537464083758587758525293663683636298511446099289911714323531235991277865531867843982015026878878896639255995472483117790417639250799634307655748030779632564691596760909404908081531372615164547043388535489528971883752026521919961407668947083407213423974005267001744595617615696630765504428689036809351860875424035621037482616342322215337934367594548330261003594070152341849674175878158149298226389343930852958679143549606816591025602305342014955716217132845940342769543266933325261087375477444832336677288699401172623142002746821390322113408026398127154228190552042800992261198805194376694845540628905243504033734992985876301276630991930510298121466339193065893457487944766698446689827603944852943168637004615202567494167670399243323951589706415785266079343514108658745562117732680244677936376530687808055754082068450365935259713154646823097603333358068171497216860297611437752243167697698110498170845456846020739750846543505713643988984041854908770199868636242151130688332117043118748773610315628039566704014306672360451008845657691837507078328158864945109567892609789595433570269036167524293587898139314884455743023719471414397201222765928299092144157389940419386413023206471168462291245388659261327754572315524233871897164259808663363315096885766828683298944863773587092451238950045819754126102259768472365375016760732714067551948222283479589066389122654198832035706196138937569618393555906093460831093138264657632808502714703693332035925939820567323717250446576767156644591421419293812145057111017406654972022884869525035195784771466087933420259995884070837958397749799705740049396202644025175073210593295755945620102108055480516934702115224195574139195098066319855672558679043638893669794670155410997428664928664168167953985116161210250297804169594684253752348013222217559814453125E-1205
3.539905630524086446192807579347028776473774255169715679364915111773183629594894261784683085891047097454394867262819826695919103869940157295563047917870123862073200485978698667987466291462969557667523695647811341020357717788109785770835114680897009767915137494440566026455659843912354456504192989188835705602179322924963912494888485940786809362154139116461482007358821801406237669112276567068833410302769662102872588881507070273133853988209179248987257143758222330905918518632062135785476152084443626925203518301132960766835299892043190742792567520558478108203462259662371962959310432074095927058133868959474972030842142538796230184620128885276788174481960845142745183370519589551950615610131186590563480089317314601509944118002032875732033201100247110459481847952990857037334823640679523424278765589162449234968023799573915320394430700449823949488280250506135330963124231931659920285823325225449622471007698126353485005939406839811953394571738969314132530527602613221562230127655722757290505931769316848401723278194683296217306185235865569986617096202061546172423665496763554374618060106337325218559850235824014071278034559473690204207288687313849631758829607486091753593924248760771142255825210360672986637626679989741155885434700486067230410700299575535149711341731609432984480384687544635995062804471136149305854175315966377810108275507681181651961962801421349165404250346126820728720208873595893440536648422148718112505064284071060898109782417605476476280361232169023368205490847212108780363322062863780340391237551435459420246337892681095889721559462791278479989710590315029810407934507056226372627879032580698604457266468300386458407724348590681589801941083795722493502969765170120695678675515620032539600652057815256453699379920243695102441630342646026753909888234075230277217434906456997302645472831528972628145734383468385440223694543434021735982488952306335977309013169460467883641860640785227636310262987940964022984673469826568517571050125270818176149707331140910524347851732575390841829346909410549839727953705987962211317256181818751290463639370119897704800571364206619105262584707180671238507585849999073849197663824198883130260095040819592313094603646217985433953185704099370284536904773266787536901400506007032774257894058018064436730924099462051548954038384791968747068019217638045788829854591320600007159149841443555646570855081618024360520958578760360280384849375071676855716557180607139713205883919099032478943967247930537312162289144679104372615090011632866858961375079019575267407833271784593151520703411904218370807609740113466986453924875951953906379335261873299732057632583548583707287549651159936981092460393588420646863927581217105974812665362938650418397789045364604339496313477110578729246893510209369181042422940691767021001414072916609146366788913996755700759640665234784333407084341110051886643788328930564570244055170922341783514820106649871716205106654635196708758096609371010578371733787205786872007626789590831722266502098400838009325576368679530618235118703501791946624067543742495370252936490448215272265394446201505681555959785096101950998232539156908671549704321558751663495139442633882009657071823899139741961998266368147671510768218115426296064382005662529050949281621806147707102909461952974903326945256007076524833299463667109290119355903366196219833733413695411303379451823231753263731780865552172559852473440089366781507217094187589639337530070328546865963879643466871402819511600155281016186295822285089803134472408174815427628345787525177001953125E-1506
3.303665137902827496041698814598251859956102752695243457545238134313877538516687455245209321720638823230490006282381480310772444510034045211226314330774840068284288371196446260296884583517814706659635628162189026783047191826750267043225489400771903093944259276619029927475346438031545811605946374504442097006994262477824439348857706776768617486310581249836618919417304749430900959706561113042262150517874802265433077999641328682509847882884924517121017190187846542512359062550646298184979756438755292545173270616579147035775541385671706654033870782649215204956912887364382156195690188215651083844934438738698733214152401938270023834336874746163318149027984994858285136502541795338316312101997589022837431637236440456040998512077161206236019051712081611565644859268804662919202586363218603552752695270832099692390467113884066534323925557128328604171993970853722160012919159551842690982027464367161257179808021863759049562797978645493867537105954179930724913679481936875402812572711930723882070284861941929119558170827897315859162842037326796133850257021269068336533236923850739658611999378702648242711666129597638095361076728543262011326217083677875215772308541136175153691149435654095931864251712782144524369655589716904881617799361462290466021866259726962247117462012115278442296220542386536419650551473602369400134797477831217636135509200328610036257616743356234677813111634192132502323701745358218078779524266341964037873127937848830826508508164096259725938628931626670796735885682591634721884490231165098692815205140693977889952801242893472229120340032093869453762832972291859794557911577639522395307940732945712107075755019740490962937164728167213432768492138733750524266274699077375733962811982278517502963812611476393952844259367082860095303675299020089115276657994155137119249817329420480064676642884472827028871906976341796718053546757401432467583130922019249330985492860548855596939221967584883875415697012658344666072424828836624264487286028591464953422057358292453173183196474756535257290117885536909618270459398220468815577926370607214507553414003598631496358187812992866734203795256335459900517763162601851984742584705374313070085033751129575420554964387441175846286562452089320606255797752016105950464227482065705408603260648257826551443686687882577303155206625947223484819915238490514881825717999121457137657714149353624922334220725600291951263162279298695401753356778309665095213843636885555987498807740209599646730604620908084012544399944192020917084890415757520265442239383238645767041482207162970579811093177791400644934010615516814079728309548220856096973675927990639940709613458493245984563211702111448359648433511680438377616298772795422516698822911002375856354580413147635506696458049588867868603320909589298339689100758949758682637103963988827307318959090420171350280219793288216670756666823872989199717083589126986974586632039928270417894835660021707866194543009725663505202689205234311075475898182424450167657545369789105339884283826964391562437254380134403620648901996109597800217991087232739433333005261148105778006362388323395808747788471187188991174879178895896934383118524229645808772348052329685868454757816134812834066970450014033639076242933255140291275675316837272240534404493876145292713293434547248272013302450195929488069705299508393401903558199250780015449549549844411621136134455877441994562247110233939116211180769579609101363083680816625026801067799314561701358285026548146460110929528534340290355738799466322677694228254707089730120937598182249688132410408196248344124873312357113507547279516555232213828894450359208043156415786747942667569034278302758632332792966168589148561952529827676455867620718491121519566778267284946472728779868353591764888425242532390195600063363530440734986641188564192928244738736960706468674352679639622457403160943430629924686778742180857897037492647553965360571395423889393190136792132808820964280842098926546195726573957235872072771514703732911328337271024285339124370833436519397291514689017778437236176793239632328400000098733961965128704164600778553840026356764016848771447647824695319245247442729526830585151011874390308115411481581839039062066822663560644921007465348898536790234192750403963145799934864044189453125E-1807
It can be store at least 6000 records...(with small possibility, always minimum key/2)
the implementation of Java's BigDecimal with some flaws, you can implement a own one if necessary.
Related
I have Dictionary<string,T> where string represents the key of record, and I have two other pieces of information about the record that I need to maintain for each record in the dictionary, which are the category of the record and its redundancy (how many times its repeated).
For example: the record XYZ1 is of category 1, and its repeated 1 times. therefore the implementation has to be something like this:
"XYZ1", {1,1}
Now moving on, I may encounter the same record in my dataset, therefore the value of the key has to be updated like:
"XYZ1", {1,2}
"XYZ1", {1,3}
...
Since I am processing big number of records such as 100K, I tried this approach but it seems inefficient because the extra effort of fetching the value from dictionary and then slicing {1,1} and then converting both slices into integer puts lot of overhead on the execution.
I was thinking of using binary digits to represent both category and repatation and maybe bitmask to fetch these pieces.
Edit: I tried to use object with 2 properties, and then Tuple<int,int>. Complexity got worse !
My question: is it possible to do so ?
if not (in terms of complexity) any suggestions?
What is your type T? You could define a custom type which holds the information you need (category and occurences) .
class MyInfo {
public int c { get; set; }
public int o { get; set; }
}
Dictionary<String, MyInfo> data;
Then when traversing your data you can easily check whether some key is already present. If yes, just increment the occurences, else insert a new element.
MyInfo d;
foreach (var e in elements) {
if (!data.TryGet(e.key, out d))
data.Add(e.key, new MyInfo { c = e.cat, o= 1});
else
d.o++;
}
EDIT
You could also combine the category and the number of occurences into one UInt64. For instance take the category in the higher 32 bit (ie you can have 4 billion categories) and the number of occurenes in the lower 32 bit (ie each key can occur 4 billion times)
Dictionary<string, UInt64> data;
UInt64 d;
foreach (var e in elements) {
if (!data.TryGet(e.key, out d))
data[e.key] = (e.cat << 32) + 1;
else
data[e.key] = d + 1;
}
And if you want to get the number of occurrences for one specific key you can just inspect the respective part of the value.
var d = data["somekey"];
var occurrences = d & 0xFFFFFFFF;
var category = d >> 32;
It seems like category never changes. So rather than using a simple string for the key of your dictionary, I would instead do something like:
Dictionary<Tuple<string,int>,int> where the key of the dictionary is a Tuple<string,int> where the string is the record and the int is the category. Then the value in the dictionary is just a count.
A dictionary is probably going to be the fastest data structure for what you're trying to accomplish as it has near constant time O(1) lookup and entry.
You can speed it up a little bit by using the Tuple, as now the category is part of the key and no longer a bit of information you have to access separately.
At the same time you could also keep the string as the key and store a Tuple<int,int> as the value and simply set Item1 as the category and Item2 as the count.
Either way is going to be roughly equivalent in speed. Processing 100k records in such a manner should be pretty fast either way.
on my site I allow people to buy subscriptions to my site in bulk(I call them vouchers). Once they have these vouchers, they give them to whoever and they enter that code into their account to upgrade them.
Right now I am thinking of doing 4 alphanumeric code(upper case, lower case and digits) and will have something like this
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[4];
var random = new Random();
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[random.Next(chars.Length)];
}
var finalString = new String(stringChars);
For now I think that will give me more than enough combinations and if I ever do run out I can always up the length of the code. I want to keep it short because I don't want the user to have to type in huge as numbers.
I also don't have the time to make a more elegant solution maybe were they click a link or something in their email and it activates their account and of course this would cut down on someone trying to randomly guess a voucher number.
These are things I would deal with if the site every becomes more popular.
I am wondering though how can I handle the possible duplicate generation of the same voucher. My first thought was to check the database each time a voucher is created and if it exists then make a new one.
However that seems like it could be slow. So I thought also maybe getting all the keys first and store them in memory and they check there but if the list keeps growing I might run into out of memory exceptions and all that great stuff.
So does anyone have any ideas? Or am I stuck doing one of the 2 method I listed above?
I am using nhibernate, asp.net mvc and C#.
Edit
static void Main(string[] args)
{
List<string> hold = new List<string>();
for (int i = 0; i < 10000; i++)
{
HashAlgorithm sha = new SHA1CryptoServiceProvider();
byte[] result = sha.ComputeHash(BitConverter.GetBytes(i));
string hex = null;
foreach (byte x in result)
{
hex += String.Format("{0:x2}", x);
}
hold.Add(hex.Substring(0,3));
Console.WriteLine(hex.Substring(0, 4));
}
Console.WriteLine("Number of Distinct values {0}", hold.Distinct().Count());
}
above is my attempt to try to use hashing. However I think I am missing something as it seems to have quite a bit more duplicates then expected.
Edit 2
I think I added what I was missing but not sure if this is exactly what he meant. I am also not sure what to do in a situation when I moved it as far as I can move it(my has seems to give me a length of 40 places I can move it).
static void Main(string[] args)
{
int subStringLength = 4;
List<string> hold = new List<string>();
for (int i = 0; i < 10000; i++)
{
SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
byte[] result = sha.ComputeHash(BitConverter.GetBytes(i));
string hex = null;
foreach (byte x in result)
{
hex += String.Format("{0:x2}", x);
}
int startingPositon = 0;
string possibleVoucherCode = hex.Substring(startingPositon,subStringLength);
string voucherCode = Move(subStringLength, hold, hex, startingPositon, possibleVoucherCode);
hold.Add(voucherCode);
}
Console.WriteLine("Number of Distinct values {0}", hold.Distinct().Count());
}
private static string Move(int subStringLength, List<string> hold, string hex, int startingPositon, string possibleVoucherCode)
{
if (hold.Contains(possibleVoucherCode))
{
int newPosition = startingPositon + 1;
if (newPosition <= hex.Length)
{
if ((newPosition + subStringLength) > hex.Length)
{
possibleVoucherCode = hex.Substring(newPosition, subStringLength);
return Move(subStringLength, hold, hex, newPosition, possibleVoucherCode);
}
// return something
return "0";
}
else
{
// return something
return "0";
}
}
else
{
return possibleVoucherCode;
}
}
}
It is going to be slow because you want to generate the vouchers randomly and then check the database for every generated code.
I would create a table vouchers with an id, the code and an is_used column. I would fill that table once with enough random codes. Since this can be done in a separate process, the performance won't be such a big problem. Let it run in the evening and the next day you get a fully filled vouchers-table.
If you want to prevent generating duplicate vouchers, that won't be a problem. You can generate them anyway and put them either in a System.Collections.Generic.HashSet (which prevents adding duplicates without throwing an exception) or call the Linq-method Distinct(), before adding them to that vouchers table.
If you insist on short codes:
Use a GUID as a primary key, generate one random number. How you might want to translate this in to alpha-num is up to you.
Use the last byte or two of the guid and the random number. 1234-684687 This should make it slightly less easy to bruteforce coupons. And handle any (rare) collisions with an exception.
Easy way to shorten an int, change it's base (from 10 to 62). (in VB, and this is old code)
This yields "2lkCB1" when given Int32.MaxValue
''//given intValue as your random integer
Dim result As String = String.Empty
Dim digits as String = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
Dim x As Integer
While (intValue > 0)
x = intValue Mod digits.Length
result = digits(x) & result
intValue = intValue - x
intValue = intValue \ digits.Length
End While
Return result
But now we're already answering more than one question.
For a bulk data operation like this, I would recommend not using NHibernate and just doing straight ADO.NET.
Batch Check
Since you anticipate generating big batches of codes at once, you should batch multiple code checks into a single round-trip to the database. If you're using SQL Server 2008 or higher, you could do this using table-valued parameters, checking a whole list of codes at once.
SELECT DISTINCT b.Code
FROM #batch b
WHERE NOT EXISTS (
SELECT v.Code
FROM dbo.Voucher v
WHERE v.Code = b.Code
);
Concurrency
Now, what about concurrency issues? What if two users generate the same code at roughly the same time? Or simply in-between the time when we check the code for uniqueness and when we insert it into the Voucher table?
We can take care of that by modifying the query as follows:
DECLARE #batchid uniqueidentifier;
SET #batchid = NEWID();
INSERT INTO dbo.Voucher (Code, BatchId)
SELECT DISTINCT b.Code, #batchid
FROM #batch b
WHERE NOT EXISTS (
SELECT Code
FROM dbo.Voucher v
WHERE b.Code = v.Code
);
SELECT Code
FROM dbo.Voucher
WHERE BatchId = #batchid;
Executing via .NET
Assuming that you have defined the following table-valued user type...
CREATE TYPE dbo.VoucherCodeList AS TABLE (
Code nvarchar(8) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL
/* !!! Remember to specify the collation on your Voucher.Code column too, since you want upper and lower-case codes. */
);
... you could execute this query via .NET code like this:
public ICollection<string> GenerateCodes(int numberOfCodes)
{
var result = new List<string>(numberOfCodes);
while (result.Count < numberOfCodes)
{
var batchSize = Math.Min(_batchSize, numberOfCodes - result.Count);
var batch = Enumerable.Range(0, batchSize)
.Select(x => GenerateRandomCode());
var oldResultCount = result.Count;
result.AddRange(FilterAndSecureBatch(batch));
var filteredBatchSize = result.Count - oldResultCount;
var collisionRatio = ((double)batchSize - filteredBatchSize) / batchSize;
// Automatically increment length of random codes if collisions begin happening too frequently
if (collisionRatio > _collisionThreshold)
CodeLength++;
}
return result;
}
private IEnumerable<string> FilterAndSecureBatch(IEnumerable<string> batch)
{
using (var command = _connection.CreateCommand())
{
command.CommandText = _sqlQuery; // the concurrency-safe query listed above
var metaData = new[] { new SqlMetaData("Code", SqlDbType.NVarChar, 8) };
var param = command.Parameters.Add("#batch", SqlDbType.Structured);
param.TypeName = "dbo.VoucherCodeList";
param.Value = batch.Select(x =>
{
var record = new SqlDataRecord(metaData);
record.SetString(0, x);
return record;
});
using (var reader = command.ExecuteReader())
while (reader.Read())
yield return reader.GetString(0);
}
}
Performance
After implementing all of this (and moving the command and parameter creation out of the loop so it would be re-used between batches), I was able to insert 10,000 codes using a batch size of 500 consistently in approx. 0.5 to 2 seconds, or 5 to 20 codes per millisecond.
Code Density / Collisions / Guessability
The _collisionThreshold field limits the density of your codes. It's a value between 0 and 1. Actually, it must be less than 1 or else you would wind up in an infinite loop when the 4 digit codes were exhausted (probably should add an assertion for this in code). I would recommend never turning it above 0.5 for performance reasons. More than 50% collisions would mean it's spending more time testing already-used codes than actually generating new ones.
Keeping the collision threshold low is how you would control how hard-to-guess your codes are. Setting _collisionThreshold to 0.01 would generate codes such that there's approximately a 1% chance of someone guessing a code.
If collisions occur too frequently, CodeLength (which is used by the GenerateRandomCode() method) will be incremented. This value needs to be persisted somewhere. After executing GenerateCodes(), check CodeLength to see if it has changed and then save the new value.
Source Code
The full code is available here: https://gist.github.com/3217856. I am the author of this code, and am releasing it under the MIT license. I had fun with this little challenge, and also got to learn how to pass a table-valued parameter to an inline parametrized query. I hadn't ever done that before. I've only ever passed them to full-fledged stored procedures.
A possible solution for you is like this:
Find the maximum ID of a voucher (an integer). Then, run any hash function on it, take the first 32 bits and convert to the string you want to show the user (or use a 32bit hash function such as Jenkins hash function). This will probably work, hash collisions are pretty rare. But this solution is very similar to yours, in the point of randomness.
You could run a test which finds the first 10 or 100 collisions (this should be enough for you) and forces the algorithm to "skip" them and use a different starting value. Then, you don't need to check the database at all (well, at least until you reach about 4294967296 vouchers...)
how about utilizing nHibernate's HiLo algorithm?
Here is an example on how you can get the next value (without DB access).
I have a scenario at work where we have several different tables of data in a format similar to the following:
Table Name: HingeArms
Hght Part #1 Part #2
33 S-HG-088-00 S-HG-089-00
41 S-HG-084-00 S-HG-085-00
49 S-HG-033-00 S-HG-036-00
57 S-HG-034-00 S-HG-037-00
Where the first column (and possibly more) contains numeric data sorted ascending and represents a range to determine the proper record of data to get (e.g. height <= 33 then Part 1 = S-HG-088-00, height <= 41 then Part 1 = S-HG-084-00, etc.)
I need to lookup and select the nearest match given a specified value. For example, given a height = 34.25, I need to get second record in the set above:
41 S-HG-084-00 S-HG-085-00
These tables are currently stored in a VB.NET Hashtable "cache" of data loaded from a CSV file, where the key for the Hashtable is a composite of the table name and one or more columns from the table that represent the "key" for the record. For example, for the above table, the Hashtable Add for the first record would be:
ht.Add("HingeArms,33","S-HG-088-00,S-HG-089-00")
This seems less than optimal and I have some flexibility to change the structure if necessary (the cache contains data from other tables where direct lookup is possible... these "range" tables just got dumped in because it was "easy"). I was looking for a "Next" method on a Hashtable/Dictionary to give me the closest matching record in the range, but that's obviously not available on the stock classes in VB.NET.
Any ideas on a way to do what I'm looking for with a Hashtable or in a different structure? It needs to be performant as the lookup will get called often in different sections of code. Any thoughts would be greatly appreciated. Thanks.
A hashtable is not a good data structure for this, because items are scattered around the internal array according to their hash code, not their values.
Use a sorted array or List<T> and perform a binary search, e.g.
Setup:
var values = new List<HingeArm>
{
new HingeArm(33, "S-HG-088-00", "S-HG-089-00"),
new HingeArm(41, "S-HG-084-00", "S-HG-085-00"),
new HingeArm(49, "S-HG-033-00", "S-HG-036-00"),
new HingeArm(57, "S-HG-034-00", "S-HG-037-00"),
};
values.Sort((x, y) => x.Height.CompareTo(y.Height));
var keys = values.Select(x => x.Height).ToList();
Lookup:
var index = keys.BinarySearch(34.25);
if (index < 0)
{
index = ~index;
}
var result = values[index];
// result == { Height = 41, Part1 = "S-HG-084-00", Part2 = "S-HG-085-00" }
You can use a sorted .NET array in combination with Array.BinarySearch().
If you get a non negative value this is the index of exact match.
Otherwise, if result is negative use formula
int index = ~Array.BinarySearch(sortedArray, value) - 1
to get index of previous "nearest" match.
The meaning of nearest is defined by a comparer you use. It must be the same you used when sorting the array. See:
http://gmamaladze.wordpress.com/2011/07/22/back-to-the-roots-net-binary-search-and-the-meaning-of-the-negative-number-of-the-array-binarysearch-return-value/
How about LINQ-to-Objects (This is by no means meant to be a performant solution, btw.)
var ht = new Dictionary<string, string>();
ht.Add("HingeArms,33", "S-HG-088-00,S-HG-089-00");
decimal wantedHeight = 34.25m;
var foundIt =
ht.Select(x => new { Height = decimal.Parse(x.Key.Split(',')[1]), x.Key, x.Value }).Where(
x => x.Height < wantedHeight).OrderBy(x => x.Height).SingleOrDefault();
if (foundIt != null)
{
// Do Something with your item in foundIt
}
Team:
I am building a business rules engine that is contextually aware -- but it's weighted -- or in other words each business rule has a level of granularity defined by the segments of a key. The segments are not combinatorial in that they cannot be weighted in any order, but rather permutable like a combination lock (interestingly enough improperly named but widely accepted).
However, to reduce the amount of code that's necessary to provide the business rules we are only building exclusion files meaning that each segment could end up with a specific key value or ALL.
So, now that we have an abstract background, let's take a concrete example. The defined segments are as follows:
Line of Business (LOB)
Company
State
Now, let's assume for this example that the LOB is ABC, Company is G and State is WY. If you break that down I should get the following permutations:
ABC_G_WY
ABC_G_ALL
ABC_ALL_WY
ABC_ALL_ALL
ALL_G_WY
ALL_G_ALL
ALL_ALL_WY
ALL_ALL_ALL
However, I need an algorithm to solve that problem. The segments must also be returned in the aforementioned order because you must always find the most finite rule first.
I look forward to your responses and thank you all in advance!
public static void Main(string[] args)
{
List<string> inputValues = new List<string>() { "ABC", "G", "WY" };
List<string> results = new List<string>();
int permutations = (int)Math.Pow(2.0, (double)inputValues.Count);
for (int i = 0; i < permutations; i++)
{
int mask = 1;
Stack<string> lineValues = new Stack<string>();
for (int j = inputValues.Count-1; j >= 0; j--, mask <<= 1)
{
if ((i & mask) == 0)
{
lineValues.Push(inputValues[j]);
}
else
{
lineValues.Push("ALL");
}
}
results.Add(string.Join("_", lineValues.ToArray())); //ToArray can go away in 4.0(?) I've been told. I'm still on 3.5
}
foreach (string s in results)
{
Console.WriteLine(s);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
}
If I get the question right, You should:
-Generate all binary strings of length N (there will be 2^N of them)
-sort them by number of bits set
-generate rules. Rule has 'ALL' in position i, if bit number i in the corresponding binary string is set
I need to generate a voucher code[ 5 to 10 digit] for one time use only. what is the best way to generate and check if been used?
edited: I would prefer alpha-numeric characters - amazon like gift voucher codes that must be unique.
When generating voucher codes - you should consider whether having a sequence which is predictable is really what you want.
For example, Voucher Codes: ABC101, ABC102, ABC103 etc are fairly predictable. A user could quite easily guess voucher codes.
To protect against this - you need some way of preventing random guesses from working.
Two approaches:
Embed a checksum in your voucher codes.
The last number on a credit card is a checksum (Check digit) - when you add up the other numbers in a certain way, lets you ensure someone has entered a number correctly. See: http://www.beachnet.com/~hstiles/cardtype.html (first link out of google) for how this is done for credit cards.
Have a large key-space, that is only sparsely populated.
For example, if you want to generate 1,000 vouchers - then a key-space of 1,000,000 means you should be able to use random-generation (with duplicate and sequential checking) to ensure it's difficult to guess another voucher code.
Here's a sample app using the large key-space approach:
static Random random = new Random();
static void Main(string[] args)
{
int vouchersToGenerate = 10;
int lengthOfVoucher = 10;
List<string> generatedVouchers = new List<string>();
char[] keys = "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890".ToCharArray();
Console.WriteLine("Vouchers: ");
while(generatedVouchers.Count < vouchersToGenerate)
{
var voucher = GenerateVoucher(keys, lengthOfVoucher);
if (!generatedVouchers.Contains(voucher))
{
generatedVouchers.Add(voucher);
Console.WriteLine("\t[#{0}] {1}", generatedVouchers.Count, voucher);
}
}
Console.WriteLine("done");
Console.ReadLine();
}
private static string GenerateVoucher(char[] keys, int lengthOfVoucher)
{
return Enumerable
.Range(1, lengthOfVoucher) // for(i.. )
.Select(k => keys[random.Next(0, keys.Length - 1)]) // generate a new random char
.Aggregate("", (e, c) => e + c); // join into a string
}
Building on the answers from Will Hughes & Shekhar_Pro (and just because I found this question interesting) here's another implementation but I've been a bit liberal with your requirement for the length of the voucher code.
Using a base 32 encoder I found you can use the Tick value to generate alpha-numeric strings. The encoding of a tick count to base 32 produces a 13 character string which can be formatted to make it more readable.
public void GenerateCodes()
{
Random random = new Random();
DateTime timeValue = DateTime.MinValue;
// Create 10 codes just to see the random generation.
for(int i=0; i<10; ++i)
{
int rand = random.Next(3600)+1; // add one to avoid 0 result.
timeValue = timeValue.AddMinutes(rand);
byte[] b = System.BitConverter.GetBytes(timeValue.Ticks);
string voucherCode = Transcoder.Base32Encode(b);
Console.WriteLine(string.Format("{0}-{1}-{2}",
voucherCode.Substring(0,4),
voucherCode.Substring(4,4),
voucherCode.Substring(8,5)));
}
}
Here's the output
AARI-3RCP-AAAAA
ACOM-AAZF-AIAAA
ABIH-LV7W-AIAAA
ADPL-26FL-AMAAA
ABBL-W6LV-AQAAA
ADTP-HFIR-AYAAA
ACDG-JH5K-A4AAA
ADDE-GTST-BEAAA
AAWL-3ZNN-BIAAA
AAGK-4G3Y-BQAAA
If you use a known seed for the Random object and remember how many codes you have already created you can continue to generate codes; e.g. if you need more codes and want to be certain you won't generate duplicates.
Here's one way: Generate a bunch of unique numbers between 10000 and 9999999999 put it in a database. Every time you give one to a user, mark it as used (or delete it if you're trying to save space).
EDIT: Generate the unique alpha-numeric values in the beginning. You'll probably have to keep them around for validation (as others have pointed out).
If your app is limited to using only Numerical digits then i think Timestamps (DateTime.Now.Ticks) can be a good way to get unique code every time. You can use random nums but that will have overhead of checking every number that its been issued already or not. If you can use alphabets also then surely go with GUID.
For checking if its been used or not you need to maintain a database and query it to check for validity.
If you prefer alphanumerical, you could use Guid.NewGuid() method:
Guid g = Guid.NewGuid();
Random rn = new Random();
string gs = g.ToString();
int randomInt = rn.Next(5,10+1);
Console.WriteLine(gs.Substring(gs.Length - randomInt - 1, randomInt));
To check if it was not used store somwhere previously generated codes and compare.
private void AutoPurchaseVouNo1()
{
try
{
int Num = 0;
con.Close();
con.Open();
string incre = "SELECT MAX(VoucherNoint+1) FROM tbl_PurchaseAllCompany";
SqlCommand command = new SqlCommand(incre, con);
if (Convert.IsDBNull(command.ExecuteScalar()))
{
Num = 100;
txtVoucherNoInt1.Text = Convert.ToString(Num);
txtVoucherNo1.Text = Convert.ToString("ABC" + Num);
}
else
{
Num = (int)(command.ExecuteScalar());
txtVoucherNoInt1.Text = Convert.ToString(Num);
txtVoucherNo1.Text = Convert.ToString("ABC" + Num);
}
con.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex, "Error !!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Try this method for creating Voucher Number like ABC100, ABC101, ABC102, etc.
Try this code
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[15];
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[random.Next(chars.Length)];
}