4

As a part of an algorithm I'm making I need to make a function that takes the current date to a function and gives out a number between 1-6, and not the same number for two consecutive dates. And it should always return the same number for a given date.

A simple solution would be to make any Monday 1, Tuesday 2, Wednesday 3, etc, but since we only have 6 values just give Sunday 3 again. And then next Monday 1.

The problem with this algorithm is that it's really predictable. A good algorithm should be harder to guess for others.

Any tips on how to approach this?

12
  • 3
    You want to map an infinite space (time) to a discrete, finite space [1..6]. This is the definition of a hash function (and indeed your simple solution is such a hash function). You could try with other types of hashes (e.g. MD5, SHA, etc.) and other ways of choosing your input (with microseconds? only weekday? etc.). In any case, if you are worried about security, this won't be enough as these functions are deterministic. Using an obscure scheme is not an option, and designing a secure scheme takes more than hashing.
    – user44761
    Jan 8, 2016 at 10:14
  • 6
    julian date modulus 6 + 1?
    – Bent
    Jan 8, 2016 at 10:15
  • 8
    How to approach this will significantly depend on the purpose, and you don't explain what it is.
    – Jan Hudec
    Jan 8, 2016 at 10:23
  • 1
    You can't guarantee that two consecutive dates won't produce the same result with (pseudo-) cryptographic hashes. You can, however, create a test program as a proof: calculate all desired hashes up to year 2100, and tweak the hash function and its parameters until no "collision" appear. I bet you won't succeed, which would demonstrate how your two requirements (non-predictability and "non-consecutiveness") are in opposition.
    – user44761
    Jan 8, 2016 at 10:28
  • 4
    @Tibo: he could make simply a randomly filled lookup table for each possible date in a useful range, where the random numbers are generated with the "consecutive constraint".
    – Doc Brown
    Jan 8, 2016 at 10:32

4 Answers 4

4

A compromise between Bent and Doc Brown's approaches might be to take the Julian Date modulus of a multiple of 6 (eg. 36), then lookup the corresponding value (between 1 to 6) in a quasi-random list that satisfies the "consecutive constraint".

For 36, such a list might be:

 246531451362532614364125615343123456

which corresponds to the end word order in a Sestina (with the verses reversed).

4
  • Elegant solution, how would you suggest to use the julian data modulus? The whole date modulus 36? ex. for first january 01012016 % 36
    – bogen
    Jan 8, 2016 at 14:33
  • @Haaakon: Sorry, that should have said "Julian date modulus 36" - now corrected.
    – user4234
    Jan 8, 2016 at 15:18
  • Ah ok, you mean just the date, so 1 % 36 for 1. january, 2 % 36 for second, etc ? @mark-bannister
    – bogen
    Jan 9, 2016 at 20:40
  • 1
    @Haaakon: This is the Julian Date I had in mind: en.wikipedia.org/wiki/Julian_day
    – user4234
    Jan 10, 2016 at 10:57
3

Build a hash based on the following formula...

2 A(d) + B(d) + 1

A(d) is a hash of the date in the range 0 to 2 inclusive.

B(d) is the least significant bit of a day-number representation of the date.

Because consecutive values of 2 A(d) never differ by exactly 1 (or any odd number), and because consecutive values of B(d) always differ by precisely 1, consecutive values of the combined result are never equal.

The easiest way to compute a suitable unpredictable A(d) is probably to use a standard hash algorithm, but take the result modulo 3. Hash algorithms generally give a result in the range 0..(2^n)-1 (or for signed arithmetic, -2^(n-1) .. (2^(n-1))-1). If you have signed hashes, make sure you're using a modulo/remainder that never gives negative results, or else correct for that. Genuine modulo, or remainder for division rounding to negative infinity, always give non-negative results. Remainders for other division rounding schemes such as the common "truncation" (toward zero) scheme may need checks to correct for that. C and C++ leave the division rounding scheme implementation-defined, so the remainder/modulo behaviour for negative values is similarly implementation-defined.

There's a small bias using modulo to convert to a 0..2 range, but that's unavoidable - 2^n distinct domain values cannot be precisely equally shared for 3 range values. The bias will be small, though, if n is anything you're likely to see in practice (32 or 64 bits).

2

Produce the number in two steps:

  • Step 1: ensure that no consecutive dates have same number
  • Step 2: make it hard to guess

For step 1 choose a serial number for each date (e.g. number of days since 1.1.2000). If this number is even, let the result be even, if the serial number is odd let the result be odd. This ensures that even and odd days alternate, therefore two consecutive days will never be the same.

For makeing hard to guess you can chose any hash function on the date which gives you one of three results.

n = serial(date)
if n is even:
   step1 = 1
else:
   step1 = 0

h = hash(date)
step2 = (h % 3) * 2 + 1

result = step1 + step2

in this pseudo code step2 is always odd. Therefore after you add step1 you get a result with the same parity as n. This ensures that you never get the same result for consecutive days.

To hide the fact that even and odd numbers alternate, you could add a permutation in the end (e.g. switch results 3 and 4)

-7

Try setting limit for 1-6 in Random class of java util package ie, Random number generator.

It may help you

/* package whatever; // don't place package name! */

import java.util.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        // your code goes here
        int n = 3,j=0;
        Random r = new Random();
        String a[] ={"sunday","monday","tuesday"};
        int i =r.nextInt(3)
        for(;;)
        {
         System.out.println(a[i]+""+i);
            j++;
            if(j>3)
            {
        System.exit(0);
            }
        }
   // check next int value  
   //System.out.println("Next int value: " + randomno.nextInt(6));
    }
}
1
  • 6
    -1. This will not output the same number for the same date (You could use the date as some form of seed though). You risk getting the same number for two consecutive days, which is against what the OP wants.
    – Niklas H
    Jan 8, 2016 at 10:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.