Select random combinations of list elements such that no list element is present in more than one selection (SQL) - javascript

I'm running a bit out of ideas how to realize a small project.
What I have:
- a list of users including their ID and name
What I want to achieve:
- I want to combine each user on this list with another user such that no user is assigned to more than one user and no user is assigned to herself.
- The combination has to be random and has to take past combinations into account
My idea so far:
- I have this information:
User (A,B,C,D) (the actual number of users ranges between 50 and 400)
Possible combinations: (A-B,A-C,A-D,B-C,B-D,C-D)
Random draw(1): (A-B, C-D)
Random draw(2): (A-D, B-C)
Random draw(3): (A-C, B-D)
I was able to get all possible combinations using a join of the user table with itself.
I guess I can take previous draws into account by storing the draws in a separate table and limit the possible combinations to those that are not in this special table.
What I can't do:
- I don't know how to randomly draw from the list of possible combinations such that every user is part of only one combination per draw (e.g. A-B,A-D in the same draw is not allowed)
- I try to use sql or a bit php for this (maybe javascript)
Thanks for any help.

An easy solution:
Create a temporary table with a row for each pairing. Loop over the list of users skipping a random number of empty rows from 1 to the number of empty rows -- insert user.
Easy solution number 2:
Given N users. Assign each user a unique random number from 1 to N (remove randomly from the set of all numbers from 1 to N). Pair each user with from 1-N/2 with user from N/2+1 to N.

The solution is the problem "Bergr (s) table", see Wikipedia: http://en.wikipedia.org/wiki/Round-robin_tournament.
The latest (best) solution is from Professor FronĨeka (Dalibor Froncek, a professor at the University of Minnesota in the US).
For custom solutions, look in the table of solutions n ^ 2.

Related

Generating uniformly distributed random numbers in JS

In my application I've got a list of words that are being shown to the user one by one. The exact number may differ, but it can get as low as about 10. It's ok if the words repeat. To select a word I currently use Math.random
const select = (arr) => arr[Math.floor(Math.random() * arr.length)]
The problem is that Math.random generates, well, random numbers, that may sometimes form clusters, so the user may see the same words several times in a row. I have a simple check that new word is different than the previous one, but I want to improve it. The question is, how can one make random numbers from that range (0 - arr.length-1) more spread, like if it was a human who would choose random words (i.e. they would repeat rarer). Or, for this particular case, how to make the user see as many different words in given time as possible?

Auto-increment dates in two cells if date has passed

Overview:
I'm not a programmer but I managed to get some serious coding into a Gsheets to track my teams project, so we have multiple-variable dropdown menus and integration with google calendar to track projects development and all that.
Why I'm at stackoverflow:
I kind of lack the knowledge to start the code from the scratch, I usually find spare parts of code through forums on the internet and clue them together and it usually works surprisingly well, but this time I couldn't find much informtation.
What I need:
I have 5 cells, and we can put as below,
Date start - Date end - date code* - number** - Priority***
*script to add the date range to gcalendar
** & *** The number is an array that's based on the word written on the priority cell, for example: If priority is written Weekly them
the number colunm will show 7 on the cell to the left and them it
goes. (monthly = 30 and blablabla...)
So I'd like to know if someone could give a hand with a script that would work (at least in my head) as following:
If I set the priority to weekly, it will show 7 on the number colunm and them, every time the "Date end" has passed, it will automatically add 7 days to the "Date start" and "Date end" again.
That way I could keep the projects on a loop where I'll be able to track them constatly.
Thanks in advance for any insights provided,
ps: I've seen some posts about this on sql, but I have no idea also on how to take advantage of the proposals that were presented there.
Edit:
Spreadsheet picture
eDIT2:
Spreadsheet with a increment colunm
Pertinent to the data set and description, you probably do not need any VBA as the increment could be achieved by adding +1 to the reference pointing to previous cell. For example, assuming cell A1 is formatted as Date, enter in cell B1: =A1+1 , then in cell C1: =B1+1 and so on. The result should be as shown below
A B C
9/1/2017 9/2/2017 9/3/2017
It could be further extended with simple logic allowing do display incremented value only if the previous cell is not empty, like : =IF(A1,A1+1,"")
In your case, it could be cell F1 containing =IF(E1,E1+1,"").
FYI, the underlying value of Date is just an Integer value (Time is represented as decimal part), so the arithmetic operations could be applied.
More generic solution would be based on the Excel DATE() Worksheet formula as shown in sample shown below (adding 1 mo. to the date entered in cell A1):
=DATE(YEAR(A1), MONTH(A1)+1, DAY(A1))
In order to implement additional logic, you may consider using Excel Worksheet IF() statement like for example, cell B1 containing:
=A1+IF(C1="week",7,1)
A B C
9/1/2017 9/8/2017 week
so based on the IF() condition it will add either 7 days if C1 contains the word "week" or 1 day otherwise. It could be further extended with nested IF().
Hope this will help.

Search with multiple range values on same item

I'm implementing a search filter via instantsearch.js with a range slider.
The slider filters a range of years based on the year data column of each item. In this DB tho there are some items with no precise year, but a "supposed" period, like "circa XVIII sec" or "End of 1910s". This means that for these items there is not a single year to filter but rather multiple years.
Let's say for ex. that I wish to filter results in a range of years from 1874 to 1930 and I have an item with "Second half 19th century" in the year field. As you can tell it should result in the match, since any year from 1851 to 1900 is correct for this item.
How should I treat this type of data to be part of this filter range?
How should I structure the data in the year column of this DB?
Should I use some script logic in the algolia javascript search rather than use a special way of structuring the data or indexing?
I was thinking of a logic like:
"If value of item is not just digits, convert it to a range of years.
Then check if the range of these years can match with the filter required in
the slider"
I can't really tell how to do this, and it looks a bit cumbersome and silly to me. I'm pretty sure this is no new issue to face in DB data design, so there has to be a proper way to address it.
I tried searching around but I don't even know how to proper word this thing I'm asking.. if you have suggestions to edit the title please do tell me!
I'm using a MySQL database along with PHP.
With Algolia, you'll need to convert this values to timestamp then you would be able to use numericFilter and add a range slider or numeric refinement list component to your UI.
Also the attribute must be defined as an attributesForFaceting in your index configuration.
https://community.algolia.com/instantsearch.js/documentation/#rangeslider
https://community.algolia.com/instantsearch.js/documentation/#numericrefinementlist
Considering your year as a range, you can either add a beggining and an end attribute to those fields. Example:
EventTimes
id SMALLINT NOT NULL PRIMARY,
yearEnd DATETIME,
yearStart DATETIME
In this case, the end and the start will be equal (I use null ends), when the event is an specific time, and becomes a range when the two fields are filled.
When SELECTing, you can always check for both fields, without a significant loss of speed or accuracy. Then check either for specific time, range of times, or if an event begins, ends, re-begins, re-ends and so on, you can check for different fields within the same table.

How to make this login/redirect method more secure

I have an application which sends e-mails to users, each e-mail containing a link to a page the user must access.
The link is an md5 of an unique id + a random number.
The link looks like: www.domain.com/index.php?id_page=<the md5>
On the index page i save the $_GET["id_page"] within a session variable, $_SESSION["id_page"] and I redirect the user to the page which he must see (the user is redirected to that page only if the link contains id_page).
How can I improve this method and make it more secure? How can I guarantee that the users enter the page designated to them - and cannot enter any other page?
What you are concerned with here is a matter of time, rather than a matter of security :). If you allow anyone to guess an infinite number of id_page values, then given enough time, eventually someone will happen upon a random valid id_page value.
Your only real defense against this is to increase the length of your hash, causing it to take (on average) more time to happen upon a random valid id_page (on the order of months or years). This could be accomplished by using sha256 or sha512 rather than md5.
Another approach is to lock someone out for a period of time if they have, for example, 3 consecutive incorrect guesses at an id_page value. This will greatly decrease the number of values they can attempt in a given period of time.
Lastly, If the user is already logged in at the time of redirect then you could also store the hashes you generate in a database table. That way you can map a particular hash to one and only one userid in the table. If a user attempts to visit a hash page to which they don't correspond in the DB, then you could redirect them elsewhere.
You can add their email in the URL. the probability of someone guessing someone else's email and the associated hash is just about 0.
One method that can work quite well for preventing guessing ID numbers is to add some sort of padding to the ID and then convert it to base32. Of course, this doesn't eliminate the ability to guess an ID entirely, but it does make it a little more time consuming for anyone who is snooping around.
If you have the URL www.domain.com/index.php?id_page=1, you could convert the id to something unique in your application, for example:
padded id = id x 9 - 2
7 = 1 x 9 - 2
16 = 2 x 9 -2
25 = 3 x 9 - 2
Then, you can convert the new padded ID to base 32, which would be
7 = G4
16 = GE3A
25 = GI2Q
The new url would then be (for an id of 1):
www.domain.com/index.php?id_page=G4
Using this method, if someone guesses the base 32 of 1-6, it would return a 404, because your ID of 1 is actually being padded out to become 7. Guessing 8-15 wouldn't return a parsed ID because the next id of 2 is padded to 16, and so on.
Not only does this keep the query string smaller in size, but it also doesn't use an obvious MD5 hash which can very easily be sequentially browsed using dictionaries.
If you want the page to be linked to a specific users, well there's no reason why you cannot append more values to the new padded_id (let's call it a hash).
For instance, if you have a user with an ID of 12, and you only want that user to be able to visit a page with an id of 10, you would need to create a hash that comprises of both these values:
page_id(10)-user_id(12), using the above example, this would produce:
(10 x 9 - 2) (12 x 9 - 2)
88-106
HA4A-GEYDM
You now have a nice small hashed link that can be secured to a single user ID. The padding method in the example above is rather simple, but it gives you the overall idea of how to approach the issue.
Don't bother hashing or creating unique ids or anything. Complexity is not going to help you.
Instead, simply generate a random token, and use that. A 256 bit random token should be sufficient for anything you need to do.
So, using mcrypt (a core extension):
$token = strtr(
base64_encode(mcrypt_create_iv(256/8, MCRYPT_DEV_URANDOM)),
'+/',
'-_'
);
That will give you a 44 character result of the alphabet a-zA-Z0-9-_ which contains 256 bits of random entropy.
There's no need to hash the result or anything. The random data is enough.
To understand why, you need to understand The Birthday Problem.
Basically with a 256 bit random value, to have a 1% chance that 2 tokens collide you would need to generate 4.8e37 tokens (that's 48 followed by 36 0's).
To get a 10e-18 chance of collision (which is typically seen as secure) you'd need to generate 4.8e29 tokens.
Since those numbers are WAY more than you'd ever generate, the chance of 2 tokens colliding is infinitesimally small.
The other problem is people guessing the token. Well, MCRYPT_DEV_URANDOM uses the underlying operating system's random pool. Which means that people are way more likely to guess your "unique id and random number" than they are to guess the token generated here.
So, in short, just use a random token and be done :-)

Javascript fuzzy filter for text-based ranges, need a regex perhaps?

I have a database of a few hundred schools (more added constantly) where one field has a verbose description of the grade levels it serves. Examples: 'K-6', '9-12', 'PK-4', 'K-3 and 6-8', 'grades 4-8'
I am rendering this data in an HTML table and using a 'select' drop-down to filter by specific grade. I would like to have the 'options' for this drop-down be single grades, such as K, 1st, 2nd, 3rd, etc. But I need a regex or some means (via javascript) to compare to this field to be sure the user-selected grade falls within the range so I can highlight the 'table' row.
BTW, I am also using the jquery library.
Try this to search the description for ranges:
/(PK|[K1-9])(\s*(-|to)\s*)(PK|[K1-9])/i
Test it.
Of course you may want to modify it and add some extra logic. It's just a starting point.
I hope this helps you,
Alin

Categories

Resources