Seeking an algorithm to efficiently layout calendar event banners - algorithm

I'm looking for an algorithm to efficiently place all-day/multi-day event banners, much like the month view in Outlook or Google Calendar. I have a number of events with a begin and end date, ordered by increasing begin (and then end) date (or any other order you please, I'm gathering events from a database table). I would like to minimize the average amount of vertical space used up, because after the event banners I will need to place other events just for that day (these always come after the banners for a given date). So, for example, if I had two events, one 1/10-1/11 and one 1/11-1/15, I would prefer to arrange them like so (each column is a single day):
bbbbb
aa
and not like:
aa
bbbbb
because when I add the events just for the day (x, y, and z), I can do this (I would prefer the first, do not want the second):
bbbbb vs. aa
aa xyz bbbbb
xyz
But it isn't as simple as placing the longer events first, because with 1/10-1/11, 1/13-1/14, and 1/11-1/13, I would want:
aa cc
bbb
as opposed to:
bbb
aa cc
because this would allow for events x and y:
aa cc vs. bbb
xbbby aa cc
x y
And of course I would prefer to do this in one pass. For the data structure, I'm currently using a map from date to list, where for each day of an event I add the event to the corresponding list. So a three-day event appears in three lists,each one under one of the days in the map. This is a convenient structure for transforming the result into visual output, but I'm open to other data structures as well. I'm currently using a greedy algorithm, where I just add each event in order, but that can produce unwanted artifacts like:
aa ccc
bbbbb
dd
eeeeeeeeeeeeeeeee
This wastes a lot of space for most of the "e" event days.
Any ideas?

Here is a high-level sketch of one possible solution (using day-of-week integers instead of full-blown dates). This interface:
public interface IEvent {
public abstract int getFirst(); // first day of event
public abstract int getLast(); // last day of event
public abstract int getLength(); // total number of days
public abstract char getLabel(); // one-char identifier
// true if this and that have NO days in common
public abstract boolean isCompatible(IEvent that);
// true if this is is compatible with all events
public abstract boolean isCompatibleWith(Collection<IEvent> events);
}
must be implemented to use the algorithm expressed in the layout method below.
In addition, the concrete class must implement Comparable to create a natural order where longer events precede shorter events. (My sample implementation for the demo below used an order of descending length, then ascending start date, then ascending label.)
The layout method takes a collection of IEvent instances and returns a Map that assigns to each row in the presentation the set of events that can be shown in that row.
public Map<Integer,Set<IEvent>> layout(Collection<IEvent> events) {
Set<IEvent> remainingEvents = new TreeSet<IEvent>(events);
Map<Integer,Set<IEvent>> result = new TreeMap<Integer,Set<IEvent>>();
int day = 0;
while (0 < remainingEvents.size()) {
Set<IEvent> dayEvents = new TreeSet<IEvent>();
for(IEvent e : remainingEvents) {
if (e.isCompatibleWith(dayEvents)) {
dayEvents.add(e);
}
}
remainingEvents.removeAll(dayEvents);
result.put(day, dayEvents);
++day;
}
return result;
}
Each row is composed by selecting the longest remaining event and progressively selecting all additional events (in the order described above) that are compatible with previously-selected events for the current row. The effect is that all events "float" upward as far as possible without collision.
The following demo shows the two scenarios in your question, along with a randomly-created set of events.
Event collection:
x(1):4
b(5):2..6
y(1):5
a(2):1..2
z(1):6
Result of layout:
0 -> {b(5):2..6}
1 -> {a(2):1..2, x(1):4, y(1):5, z(1):6}
Visual presentation:
bbbbb
aa xyz
Event collection:
x(1):1
b(3):2..4
a(2):1..2
c(2):4..5
y(1):5
Result of layout:
0 -> {b(3):2..4, x(1):1, y(1):5}
1 -> {a(2):1..2, c(2):4..5}
Visual presentation:
xbbby
aa cc
Event collection:
f(2):1..2
h(2):1..2
d(4):1..4
e(4):2..5
c(1):6
a(2):5..6
g(4):2..5
b(2):0..1
Result of layout:
0 -> {d(4):1..4, a(2):5..6}
1 -> {e(4):2..5, b(2):0..1, c(1):6}
2 -> {g(4):2..5}
3 -> {f(2):1..2}
4 -> {h(2):1..2}
Visual presentation:
ddddaa
bbeeeec
gggg
ff
hh

I think in a situation like this, you're much better off making sure your data is organized properly first and then rendering it. I know you want a single pass, but I think the results would be alot better.
For instance, organize the data into the lines you'll need to have for a given day and organize the events the best way possible, starting with the longest events (don't need to be displayed first, but they do need to be organized first) and moving down to the shortest events. This will allow you to render your output accordingly not wasting any space, and avoiding those "e" event days. Additionally, then:
bbb
aa cc
or
aa cc
bbb
won't matter because x and y can always go on either side of bbb or even between aa and cc
I hope you find this helpful.

Related

Finding optimal swapping paths in employees moving to different cities

We have a problem where we want to find the optimal path for swapping employees' locations across the country.
Hypothetically, a company allows for employees to request to move to another city only if a vacancy is available in that city, and also if someone is willing to take their soon-to-be vacant position. Examine the example:
Employee A who currently works in Los Angeles wants to move to Boston.
Employee B who currently works in Boston wants to move to New York.
Employee C who currently works in New York wants to move to Los Angeles.
In the above triangle, we can grant all three employees the permission to do the move, since there won't be any vacancies once they move. But the situation gets more complex when:
Multiple employees are competing for the same location. We can solve this with a hypothetical score of some sort, like more years working for the company gets the priority.
We have more cities to consider. (in the hundreds)
We have more employees to consider. (in the hundreds of thousands)
Ultimately the goal is to grant the highest number of move permissions without leading to any vacancies in the system.
We're currently exploring the idea of simulating all the swapping paths, and then selecting the one that generates the highest number of moves.
But I feel that this problem existed in the wild before, I just don't know what keywords to look for in order to get more insights. Any ideas? What algorithms should we look into?
Remove the impossible move requests, like this
A,B are specific cities. n is amy city
RAB is a request to move from A to B
RAn is a request to move from A
RnA is a reuest to move to A
CAn is the number of requests to move from A
CnA is the number of requests to move to A
set flag TRUE
WHILE ( flag == TRUE )
set flag = FALSE
LOOP A over all cities
IF CAn > CnA then not all RAn can be permitted.
Remove lower scoring requests until CAn == CnA.
set flag TRUE
Once these "impossible" moves are removed, all of the remaining move requests are "in-balance". That is, all of the move requests to a city are equal all of those from a city. From that point on it no longer matters which cycles you choose to implement: once you implement them, the remainings requests are still all in-balance. And no matter which move-cycle and which order they are implemented in, it stays in-balance until all remaining requests are zero, and the total number of moves will be exactly the same no matter how they are implemented. ( This explanation is due to https://stackoverflow.com/users/109122/rbarryyoung )
Here is C++ code implementing this
void removeForbidden()
{
bool flag = true;
while (flag)
{
flag = false;
for (auto &city : sCity)
{
auto vFrom = RequestCountFrom(city);
auto vTo = RequestCountTo(city);
if (vFrom.size() > vTo.size())
{
for (int k = vTo.size(); k < vFrom.size(); k++)
{
vFrom[k]->allowed = false;
}
flag = true;
}
}
}
std::cout << "Permitted moves:\n";
for (auto &R : vRequest)
{
if (R.allowed)
std::cout << R.text();
}
}
The complete application code is at https://gist.github.com/JamesBremner/5f49beaca59a7a7043e356fbb35f0d09
The input is a space delimited text file with 4 columns: employee name, employee score, from city, to city
Here is sample input based on your example but adding another request that cannot be permitted
e1 1 a b
e2 1 b c
e3 1 c a
e4 0 a c
The output from this is
Permitted moves:
e1 1 a b
e2 1 b c
e3 1 c a
Note: I have not implemented the scoring. For simplicity I assume that move requests are entered in order of descending score. So, the requests that are dropped when necessary, change according to the order you enter them. I assume you will be able to implement whatever scoring system you require. Also note that, unless you calculate a unique score for every request from a city, then which requests are denied may vary with the order of input.
I was about to post this in a comment but it was more than the the actually allowed characters.
I'm not sure about existing advanced algorithms that could potentially solve this problem, but you can custom fit some fundamental ones:
An employee wanting to move from city1 to some city2 is a directed edge from city1 to city2. Make sure that if 2 employees want to move from A to B, you add 2 directed edges for that or somehow keep count of the quantity.
Find disjoint components of the graph.
In each disjoint component, find the largest possible circle. A circle means A -> B -> C -> A.
Remove those edges and keep count of the number of successful swaps.
Rpeat until there are no circles in any of the disjoint components.
This is a greedy algorithm. At the moment I'm still not quite sure if it would produce the optimal solution in each and every situation. Any input is appreciated.

Split test groups base on GUID

Users in the system are identified by GUID, and with a new feature, I want to divide users into two groups - test and control.
Is there a easy way to split users into one of the two group with a 50/50 chance, based on their GUID?
e.g. If the nth character's ascii code is an odd -> test group, otherwise control group.
What about 70/30, or other ratio?
The reason I want to classify users base on GUID, is because later I can easily tell which users are in which group and compare the performance between two groups, without having to keep track of the group assignment - I simply need to calculate it again.
As Derek Li notes, the GUID's bits might be based on a timestamp, so you shouldn't use them directly.
The safest solution is to hash the GUID using a hash function like MurmurHash. This will produce a random number (but the same random number every time for any given GUID) which you can then use to do the split.
For example, you could do a 30/70 split like this:
function isInTestGroup(user) {
var hash = murmurHash(user.guid);
return (hash % 100) < 30;
}
If some character in the GUID has a 1 in 16 change of being one of the following characters: "0123456789ABCEDF", then perhaps you could test a scheme that determines placement by that character.
Say the last character of the guid called c has a 1/16 chance of being any hex digit:
for 50/50 distribution -> c <= 7 for group 1, c > 7 for group 2
for 70/30 c <= A for group 1, c > A for group 2
etc...

Algorithm to create unique random concatenation of items

I'm thinking about an algorithm that will create X most unique concatenations of Y parts, where each part can be one of several items. For example 3 parts:
part #1: 0,1,2
part #2: a,b,c
part #3: x,y,z
And the (random, one case of some possibilities) result of 5 concatenations:
0ax
1by
2cz
0bz (note that '0by' would be "less unique " than '0bz' because 'by' already was)
2ay (note that 'a' didn't after '2' jet, and 'y' didn't after 'a' jet)
Simple BAD results for next concatenation:
1cy ('c' wasn't after 1, 'y' wasn't after 'c', BUT '1'-'y' already was as first-last
Simple GOOD next result would be:
0cy ('c' wasn't after '0', 'y' wasn't after 'c', and '0'-'y' wasn't as first-last part)
1az
1cx
I know that this solution limit possible results, but when all full unique possibilities will gone, algorithm should continue and try to keep most avaible uniqueness (repeating as few as possible).
Consider real example:
Boy/Girl/Martin
bought/stole/get
bottle/milk/water
And I want results like:
Boy get milk
Martin stole bottle
Girl bought water
Boy bought bottle (not water, because of 'bought+water' and not milk, because of 'Boy+milk')
Maybe start with a tree of all combinations, but how to select most unique trees first?
Edit: According to this sample data, we can see, that creation of fully unique results for 4 words * 3 possibilities, provide us only 3 results:
Martin stole a bootle
Boy bought an milk
He get hard water
But, there can be more results requested. So, 4. result should be most-available-uniqueness like Martin bought hard milk, not Martin stole a water
Edit: Some start for a solution ?
Imagine each part as a barrel, wich can be rotated, and last item goes as first when rotates down, first goes as last when rotating up. Now, set barells like this:
Martin|stole |a |bootle
Boy |bought|an |milk
He |get |hard|water
Now, write sentences as We see, and rotate first barell UP once, second twice, third three and so on. We get sentences (note that third barell did one full rotation):
Boy |get |a |milk
He |stole |an |water
Martin|bought|hard|bootle
And we get next solutions. We can do process one more time to get more solutions:
He |bought|a |water
Martin|get |an |bootle
Boy |stole |hard|milk
The problem is that first barrel will be connected with last, because rotating parallel.
I'm wondering if that will be more uniqe if i rotate last barrel one more time in last solution (but the i provide other connections like an-water - but this will be repeated only 2 times, not 3 times like now). Don't know that "barrels" are good way ofthinking here.
I think that we should first found a definition for uniqueness
For example, what is changing uniqueness to drop ? If we use word that was already used ? Do repeating 2 words close to each other is less uniqe that repeating a word in some gap of other words ? So, this problem can be subjective.
But I think that in lot of sequences, each word should be used similar times (like selecting word randomly and removing from a set, and after getting all words refresh all options that they can be obtained next time) - this is easy to do.
But, even if we get each words similar number od times, we should do something to do-not-repeat-connections between words. I think, that more uniqe is repeating words far from each other, not next to each other.
Anytime you need a new concatenation, just generate a completely random one, calculate it's fitness, and then either accept that concatenation or reject it (probabilistically, that is).
const C = 1.0
function CreateGoodConcatenation()
{
for (rejectionCount = 0; ; rejectionCount++)
{
candidate = CreateRandomConcatination()
fitness = CalculateFitness(candidate) // returns 0 < fitness <= 1
r = GetRand(zero to one)
adjusted_r = Math.pow(r, C * rejectionCount + 1) // bias toward acceptability as rejectionCount increases
if (adjusted_r < fitness)
{
return candidate
}
}
}
CalculateFitness should never return zero. If it does, you might find yourself in an infinite loop.
As you increase C, less ideal concatenations are accepted more readily.
As you decrease C, you face increased iterations for each call to CreateGoodConcatenation (plus less entropy in the result)

Are there sequence-operator implementations in .NET 4.0?

With that I mean similar to the Linq join, group, distinct, etc. only working on sequences of values, not collections.
The difference between a sequence and a collection is that a sequence might be infinite in length, whereas a collection is finite.
Let me give you an example:
var c1 = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var c2 = FunctionThatYieldsFibonacciNumbers();
var c3 = c1.Except(c2);
This does not work. The implementation of Except does not work on the basis that the numbers in either collection will be strictly ascending or descending, so it first tries to gather all the values from the second collection into a set (or similar), and only after that will it start enumerating the first collection.
Assuming that the function above is just a While-loop that doesn't terminate unless you explicitly stop enumerating it, the above code will fail with a out-of-memory exception.
But, given that I have collections that are considered to be strictly ascending, or descending, are there any implementations already in .NET 4.0 that can do:
Give me all values common to both (inner join)
Give me all values of both (union/outer join)
Give me all values in sequence #1 that isn't in sequence #2
I need this type of functionality related to a scheduling system I need to build, where I need to do things like:
c1 = the 1st and 15th of every month from january 2010 and onwards
c2 = weekdays from 2010 and onwards
c3 = all days in 2010-2012
c4 = c1 and c2 and c3
This would basically give me every 1st and 15th of every month in 2010 through 2012, but only when those dates fall on weekdays.
With such functions it would be much easier to generate the values in question without explicitly having to build collections out of them. In the above example, building the first two collections would need to know the constraint of the third collection, and the examples can become much more complex than the above.
I'd say that the LINQ operators already work on general sequences - but they're not designed to work specifically for monotonic sequences, which is what you've got here.
It wouldn't be too hard to write such things, I suspect - but I don't believe anything's built-in; there isn't even anything for this scenario in System.Interactive, as far as I can see.
You may onsider the Seq module of F#, which is automatically invoked by using special F# language constructs like 1 .. 10 which generates a sequence. It supports infinite sequences the way you describe, because it allows for lazy evaluation. Using F# may or may not be trivial in your situation. However, it shouldn't be too hard to use the Seq module directly from C# (but I haven't tried it myself).
Following this Mandelbrot example shows a way to use infinite sequences with C# by hiding yield. Not sure it brings you closer to what you want, but it might help.
EDIT
While you already commented that it isn't worthwhile in your current project and accepted an answer to your question, I was intrigued by the idea and conjured up a little example.
It appeared to be rather trivial and works well in C# with .NET 3.5 and .NET 4.0, by simple including FSharp.Core.dll (download it for .NET 3.5) to your references. Here's an out-of-the box example of an infinite sequence implementing your first use-case:
// place in your using-section:
using Microsoft.FSharp.Collections;
using Microsoft.FSharp.Core;
// [...]
// trivial 1st and 15th of the month filter, starting Jan 1, 2010.
Func<int, DateTime> firstAndFifteenth = (int i) =>
{
int year = i / 24 + 2010;
int day = i % 2 != 0 ? 15 : 1;
int month = ((int)i / 2) % 12 + 1;
return new DateTime(year, month, day);
};
// convert func to keep F# happy
var fsharpFunc = FSharpFunc<int, DateTime>.FromConverter(
new Converter<int, DateTime>(firstAndFifteenth));
// infinite sequence, returns IEnumerable
var infSeq = SeqModule.InitializeInfinite<DateTime>(fsharpFunc);
// first 100 dates
foreach (var dt in infSeq.Take(100))
Debug.WriteLine("Date is now: {0:MM-dd-yyy}", dt);
Output is as can be expected, first few lines like so:
Date is now: 01-01-2010
Date is now: 01-15-2010
Date is now: 02-01-2010
Date is now: 02-15-2010
Date is now: 03-01-2010

Speed dating algorithm

I work in a consulting organization and am most of the time at customer locations. Because of that I rarely meet my colleagues. To get to know each other better we are going to arrange a dinner party. There will be many small tables so people can have a chat. In order to talk to as many different people as possible during the party, everybody has to switch tables at some interval, say every hour.
How do I write a program that creates the table switching schedule? Just to give you some numbers; in this case there will be around 40 people and there can be at most 8 people at each table. But, the algorithm needs to be generic of course
heres an idea
first work from the perspective of the first person .. lets call him X
X has to meet all the other people in the room, so we should divide the remaining people into n groups ( where n = #_of_people/capacity_per_table ) and make him sit with one of these groups per iteration
Now that X has been taken care of, we will consider the next person Y
WLOG Y be a person X had to sit with in the first iteration itself.. so we already know Y's table group for that time-frame.. we should then divide the remaining people into groups such that each group sits with Y for every consecutive iteration.. and for each iteration X's group and Y's group have no person in common
.. I guess, if you keep doing something like this, you will get an optimal solution (if one exists)
Alternatively you could crowd source the problem by giving each person a card where they could write down the names of all the people they got dine with.. and at the end of event, present some kind of prize to the person with the most names in their card
This sounds like an application for genetic algorithm:
Select a random permutation of the 40 guests - this is one seating arrangement
Repeat the random permutation N time (n is how many times you are to switch seats in the night)
Combine the permutations together - this is the chromosome for one organism
Repeat for how ever many organisms you want to breed in one generation
The fitness score is the number of people each person got to see in one night (or alternatively - the inverse of the number of people they did not see)
Breed, mutate and introduce new organisms using the normal method and repeat until you get a satisfactory answer
You can add in any other factors you like into the fitness, such as male/female ratio and so on without greatly changing the underlying method.
Why not imitate real world?
class Person {
void doPeriodically() {
do {
newTable = random (numberOfTables);
} while (tableBusy(newTable))
switchTable (newTable)
}
}
Oh, and note that there is a similar algorithm for finding a mating partner and it's rumored to be effective for those 99% of people who don't spend all of their free time answering programming questions...
Perfect Table Plan
You might want to have a look at combinatorial design theory.
Intuitively I don't think you can do better than a perfect shuffle, but it's beyond my pre-coffee cognition to prove it.
This one was very funny! :D
I tried different method but the logic suggested by adi92 (card + prize) is the one that works better than any other I tried.
It works like this:
a guy arrives and examines all the tables
for each table with free seats he counts how many people he has to meet yet, then choose the one with more unknown people
if two tables have an equal number of unknown people then the guy will choose the one with more free seats, so that there is more probability to meet more new people
at each turn the order of the people taking seats is random (this avoid possible infinite loops), this is a "demo" of the working algorithm in python:
import random
class Person(object):
def __init__(self, name):
self.name = name
self.known_people = dict()
def meets(self, a_guy, propagation = True):
"self meets a_guy, and a_guy meets self"
if a_guy not in self.known_people:
self.known_people[a_guy] = 1
else:
self.known_people[a_guy] += 1
if propagation: a_guy.meets(self, False)
def points(self, table):
"Calculates how many new guys self will meet at table"
return len([p for p in table if p not in self.known_people])
def chooses(self, tables, n_seats):
"Calculate what is the best table to sit at, and return it"
points = 0
free_seats = 0
ret = random.choice([t for t in tables if len(t)<n_seats])
for table in tables:
tmp_p = self.points(table)
tmp_s = n_seats - len(table)
if tmp_s == 0: continue
if tmp_p > points or (tmp_p == points and tmp_s > free_seats):
ret = table
points = tmp_p
free_seats = tmp_s
return ret
def __str__(self):
return self.name
def __repr__(self):
return self.name
def Switcher(n_seats, people):
"""calculate how many tables and what switches you need
assuming each table has n_seats seats"""
n_people = len(people)
n_tables = n_people/n_seats
switches = []
while not all(len(g.known_people) == n_people-1 for g in people):
tables = [[] for t in xrange(n_tables)]
random.shuffle(people) # need to change "starter"
for the_guy in people:
table = the_guy.chooses(tables, n_seats)
tables.remove(table)
for guy in table:
the_guy.meets(guy)
table += [the_guy]
tables += [table]
switches += [tables]
return switches
lst_people = [Person('Hallis'),
Person('adi92'),
Person('ilya n.'),
Person('m_oLogin'),
Person('Andrea'),
Person('1800 INFORMATION'),
Person('starblue'),
Person('regularfry')]
s = Switcher(4, lst_people)
print "You need %d tables and %d turns" % (len(s[0]), len(s))
turn = 1
for tables in s:
print 'Turn #%d' % turn
turn += 1
tbl = 1
for table in tables:
print ' Table #%d - '%tbl, table
tbl += 1
print '\n'
This will output something like:
You need 2 tables and 3 turns
Turn #1
Table #1 - [1800 INFORMATION, Hallis, m_oLogin, Andrea]
Table #2 - [adi92, starblue, ilya n., regularfry]
Turn #2
Table #1 - [regularfry, starblue, Hallis, m_oLogin]
Table #2 - [adi92, 1800 INFORMATION, Andrea, ilya n.]
Turn #3
Table #1 - [m_oLogin, Hallis, adi92, ilya n.]
Table #2 - [Andrea, regularfry, starblue, 1800 INFORMATION]
Because of the random it won't always come with the minimum number of switch, especially with larger sets of people. You should then run it a couple of times and get the result with less turns (so you do not stress all the people at the party :P ), and it is an easy thing to code :P
PS:
Yes, you can save the prize money :P
You can also take look at stable matching problem. The solution to this problem involves using max-flow algorithm. http://en.wikipedia.org/wiki/Stable_marriage_problem
I wouldn't bother with genetic algorithms. Instead, I would do the following, which is a slight refinement on repeated perfect shuffles.
While (there are two people who haven't met):
Consider the graph where each node is a guest and edge (A, B) exists if A and B have NOT sat at the same table. Find all the connected components of this graph. If there are any connected components of size < tablesize, schedule those connected components at tables. Note that even this is actually an instance of a hard problem known as Bin packing, but first fit decreasing will probably be fine, which can be accomplished by sorting the connected components in order of biggest to smallest, and then putting them each of them in turn at the first table where they fit.
Perform a random permutation of the remaining elements. (In other words, seat the remaining people randomly, which at first will be everyone.)
Increment counter indicating number of rounds.
Repeat the above for a while until the number of rounds seems to converge.

Resources