• Coding
  • [Exercise] An Interview Question

@CSGeek
You're left with 3 uncertain coins.
1b follows 1a (it is the case where you compare 3 good coins against 3 uncertain coins and the weight is not the same, hence you are sure that you have 3 uncertain coins and the rest are all good!)
scatman wrote@CSGeek
You're left with 3 uncertain coins.
1b follows 1a (it is the case where you compare 3 good coins against 3 uncertain coins and the weight is not the same, hence you are sure that you have 3 uncertain coins and the rest are all good!)
Ahh, you're right.
Offtopic: @Admin Adding an equation tool to the forum may be useful :)

@mesa177 You were right, I had a mistake in the end. ok well there it is.


A little explanation:

In the following I supposed (1234)<(5678).
so the !coin is an element of (12345678)

Step #2: (156) && (278)
If balance ==>
Step #3: (4) && (9) if it balance then the different coin is 3 if not it's 4

If !balance ==>
Step #3: I suppose (156)<(278)
Option A: 7 or 8 are the different coins and they are heavier.
Option B: 1 is the different coin and it is lighter.

(7)&&(9)
If balance ==> 1 is the different (lighter)
if !balance ==> 7 or 8 is the different (the heavier one)

I am not sure if it's clear enough...

LINK
scatman wroteyes there is a solution (even if you don't know if its heavier or lighter)

Define:
good coin --> a coin with normal weigh
bad coin --> either a heavy or a light coin
uncertain coin --> a coin that you don't know of it's a good coin or a bad coin

you start by weighing 4 coins against other 4 coins

1- if the weight is the same then you got 8 good coins and 4 uncertain coins
you then weigh 3 good coins against 3 uncertain coins
1a - if the weight is the same, then you are left with 1 bad coin. you weigh the bad coin with a good coin to determine if its heavier or lighter
1b - if the weight is not the same, then now you know if the coin is heavier or lighter and you are left with 3 uncertain coins.
so now weigh one uncertain coin with another uncertain coin to determine which of the three uncertain coins is the bad coin.

2- if the weight is not the same --> i'll leave this for someone else (i know the answer)
For the part in bold, how can you tell if the coin is heavier or lighter? The 3 good coins can weight either heavier or lighter than the set of 2 good coins and 1 bad coin. There's not enough data to tell.

@scorz: where are coins 10 through 12? If 4 and 9 are balanced in stage 3, this doesn't mean that coin 3 is different because you still haven't included coins 10 through 12. Besides, you assumed that coin 9 is good. Check your solution again.

My solution link; note the only flaw is highlighted on the analysis chart, so any help is welcomed :(
Yes mesa
because in 1b you are comparing 3 good coins against 3 uncertain coins and the weights don't match (are not equal)
so if the 3 uncertain coins weigh less --> the bad coin is lighter
else if the 3 uncertain coins weigh more --> the bad coin is heavier
scatman wroteYes messa
because in 1b you are comparing 3 good coins with 3 uncertain coins and the weights don't match (are not equal)
so if the 3 uncertain coins weigh less --> the bad coin is lighter
else if the 3 uncertain coins weigh more --> the bad coin is heavier
Ah, now I get it (duh, I'm such a clutz). But until the other part of the solution is revealed, I'm still skeptical about having a final solution where the faulty coin is always identified (unlike the hang I had in mine). Does your approach do so?...... Oh, I'll do it again your way and see for my self...
Yes i got a solution for the second part.. but i won't reveal it now:P
@mesa177 That was just a correction to the second part =)

It's the " hypotheses: Not Balanced " from the first Step.

The complete solution is in the first LINK
Remove the " Not Balanced " from the first step in the tree and add the new one...

Anyways, my solution is so messed now. I'll check your solution soon and try to write a new one and re-post the pic.

Happy Hacking!

EDIT: Complete new solution
There are many ways to solve this puzzle...
Here are very 2 similar solutions:
Don't read them if you really want to solve them
1st
2nd // this one is similar to scorz's solution
var all = [1,1,1,1,1,1,1,1,1,1,1,1];
var r_index = parseInt(Math.random()*all.length);
//we randomize a weight either heavier or lighter.
all[r_index] = (Math.random() < 0.5)? 2:-1; //the missing link

$("#test").html("We are trying to find item number: " + (r_index+1) +  " which is : " + ((all[r_index]<0)? "lighter":"heavier") + " than the rest of the items");


function weightOfItems(side)
{
   for(var s = 0, i = side.length; i; s += side[--i]);
   return s;       
}

function weigh(side1, side2)
{
    var w1 = weightOfItems(side1);
    var w2 = weightOfItems(side2);
    if(w1 > w2) return "H";
    else if(w1 < w2) return "L";
    else return "E";
}

var tbl = {
       HLE:[1,"heavier"],
    LHE:[1,"lighter"],
    HEH:[2,"heavier"],
    LEL:[2,"lighter"],
    HEL:[3,"heavier"],
    LEH:[3,"lighter"],
    HEE:[4,"heavier"],
    LEE:[4,"lighter"],
    LLH:[5,"heavier"],
    HHL:[5,"lighter"],
    LLE:[6,"heavier"],
    HHE:[6,"lighter"],
    LHL:[7,"heavier"],
    HLH:[7,"lighter"],
    LHH:[8,"heavier"],
    HLL:[8,"lighter"],
    EHH:[9,"heavier"],
    ELL:[9,"lighter"],
    ELE:[10,"heavier"],
    EHE:[10,"lighter"],
    EHL:[11,"heavier"],
    ELH:[11,"lighter"],
    EEL:[12,"heavier"],
    EEH:[12,"lighter"]
    };

var array1 = all.slice(0,4);
var array2 = all.slice(4,8);
var array3 = [all[6],all[7],all[8],all[10]];
var array4 = [all[0],all[4],all[5],all[9]];
var array5 = [all[1],all[4],all[7],all[8]];
var array6 = [all[2],all[6],all[10],all[11]];
var str = weigh(array1,array2) + weigh(array3,array4) + weigh(array5,array6);

$("#result").html("result found: "+str+" which corresponds to item number: "+ tbl[str][0] + " being " + tbl[str][1] + " than the other items");
http://jsfiddle.net/rCk7B/
@scorz: Oh I didn't get that from your post, my bad.

Well this exercise is a great brain teaser :) I'm glad you posted it Ra8
@mesa177 In fact it's my bad. My post was messed up. That exercise really blew my mind.
8 days later
This algo solves the 6 balls case quickly, but I haven't been able to make it work for 12 balls in reasonable time (before heat death of the universe). I probably will try to change it from a depth first search to a breadth first, but it already took a toll on my time at my real job.

Here's the code:
http://pastie.org/5580271
scatman wroteHint: You start by weighing 4 coins against 4 other coins
aaaah!
The program generates the following for the 6 balls case (output from a slightly altered program to make it more obvious what is happening):
solved? 1
possibilities: 0-,0+,1-,1+,2-,2+,3-,3+,4-,4+,5-,5+,
compare 0 with 1 
if first is less
	possibilities: 0+,1-,
	compare 0 with 1 
	if first is less
		possibilities: 0+,1-,
		compare 0 with 2 
		if first is less
			possibilities: 0+,
		if they are equal
			possibilities: 1-,
		if first is greater
			possibilities: 
	if they are equal
		possibilities: 
	if first is greater
		possibilities: 
if they are equal
	possibilities: 2-,2+,3-,3+,4-,4+,5-,5+,
	compare 0 2 with 3 4 
	if first is less
		possibilities: 2+,3-,4-,
		compare 3 with 4 
		if first is less
			possibilities: 4-,
		if they are equal
			possibilities: 2+,
		if first is greater
			possibilities: 3-,
	if they are equal
		possibilities: 5-,5+,
		compare 0 with 5 
		if first is less
			possibilities: 5-,
		if they are equal
			possibilities: 
		if first is greater
			possibilities: 5+,
	if first is greater
		possibilities: 2-,3+,4+,
		compare 3 with 4 
		if first is less
			possibilities: 3+,
		if they are equal
			possibilities: 2-,
		if first is greater
			possibilities: 4+,
if first is greater
	possibilities: 0-,1+,
	compare 0 with 1 
	if first is less
		possibilities: 
	if they are equal
		possibilities: 
	if first is greater
		possibilities: 0-,1+,
		compare 0 with 2 
		if first is less
			possibilities: 
		if they are equal
			possibilities: 1+,
		if first is greater
			possibilities: 0-,
I constructed a complete list.: 3 measures yield 27 possibilities, 3 are impossible, 24 correspond to 12 when one is heavier, and the other 12 when one is lighter. Coins are named 1,2,3,4,5,6,7,8,9,A,B,C flag H indicates its a heavier coin, L means it's a lighter.

1a (1234) = (5678) (means when comparing coins 1,2,3,4 to 5,6,7,8, there is balance)

2a (123) = (9AB)

3a (1) = (C) => impossible
3b (1) > (C) => CL
3c (1) < (C) => CH

2b (123) > (9AB)

3a (A) = (B) => 9L
3b (A) > (B) => BL
3c (A) < (B) => AL

2a (123) < (9AB)

3a (A) = (B) => 9H
3b (A) > (B) => AH
3c (A) < (B) => BH

1b (1234) > (5678)

2a (156) = (278)

3a (4) = (9) => 3H
3b (4) > (9) => 4H
3c (4) < (9) => impossible

2b (156) > (278)

3a (7) = (8) => 1H
3b (7) > (8) => 8L
3c (7) < (8) => 7L

2b (156) < (278)

3a (5) = (6) => 2H
3b (5) > (6) => 6L
3c (5) < (6) => 5L

1c (1234) < (5678)

2a (156) = (278)

3a (4) = (9) => 3L
3b (4) > (9) => impossible
3c (4) < (9) => 4L

2b (156) > (278)

3a (5) = (6) => 2L
3b (5) > (6) => 5H
3c (5) < (6) => 6H

2b (156) < (278)

3a (7) = (8) => 1L
3b (7) > (8) => 7H
3c (7) < (8) => 8H

Lan
7 days later
I finally wrote code that could solve this problem, phew. This takes on the order of minutes to compute. The trick is in the symmetries and caching (otherwise, I wouldn't have come back here in years.)

Syntax of Output:
The list of numbers :: list of numbers means a weighing of the left list of balls versus the right list of balls.
The number[+|-] means a possible state. In the beginning, all states are possible, so the program lists all ball indices in both less weight and more weight. 8+ means the eighth ball is heavier than the rest.
In the solution, when a comparison is done, there are three possibilities: left is less that right, left is equal to right, left is greater than right.
The rest of the solution under each possibility is indented below each comparison possibility.

The output of the algo finds the first possible path only when it begins with 4 balls versus 4 balls. It tries comparing 1-1, 2-2, 3-3 and finally finds out at 4-4. I assumed that all comparisons must be made with equal number of scales on each side.

[edit1]Thought I was done with this piece, I was wrong. The algo is stepping on its own feet. Debugging this thing is not really the walk in the park.[/edit1]

Output
0 :: 1 
0 1 :: 2 3 
0 1 2 :: 3 4 5 
0 1 2 3 :: 4 5 6 7 
solved? 1
0-,0+,1-,1+,2-,2+,3-,3+,4-,4+,5-,5+,6-,6+,7-,7+,8-,8+,9-,9+,10-,10+,11-,11+,
0 1 2 3 :: 4 5 6 7 
less
	0-,1-,2-,3-,4+,5+,6+,7+,
	0 1 4 :: 2 3 5 
	less
		2-,3-,5+,
		3 :: 2 
		less
			3-,
		equal
			5+,
		greater
			2-,
	equal
		6+,7+,
		2 :: 6 
		less
			6+,
		equal
			7+,
		greater
			
	greater
		2-,3-,4+,
		2 :: 3 
		less
			2-,
		equal
			4+,
		greater
			3-,
equal
	8-,8+,9-,9+,10-,10+,11-,11+,
	0 8 :: 9 10 
	less
		8-,9+,10+,
		9 :: 10 
		less
			10+,
		equal
			8-,
		greater
			9+,
	equal
		11-,11+,
		0 :: 11 
		less
			11+,
		equal
			
		greater
			11-,
	greater
		3-,8+,10-,
		3 :: 10 
		less
			3-,
		equal
			8+,
		greater
			10-,
greater
	0+,1+,2+,3+,4-,5-,6-,7-,
	0 4 5 :: 1 6 7 
	less
		1+,6-,7-,
		7 :: 6 
		less
			7-,
		equal
			1+,
		greater
			6-,
	equal
		2+,3+,
		6 :: 2 
		less
			2+,
		equal
			3+,
		greater
			
	greater
		0+,6-,7-,
		6 :: 7 
		less
			6-,
		equal
			0+,
		greater
			7-,
Fresh code, with all the commenting and possible mistakes.
//
//  main.cpp
//  12balls
//
//  Created by arithma on 12/24/12.
//  Copyright (c) 2012 napkin. All rights reserved.
//

#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <assert.h>
using namespace std;

const int k = 12;

struct state{
    int index;
    int weight;
    
    bool operator<(state const &  other) const{
        if(index < other.index)
            return true;
        else if(index == other.index){
            return weight < other.weight;
        }
        else{
            return false;
        }
    }
};

struct test{
    set<int> left;
    set<int> right;
    
    test() {}
    test(set<int> _left, set<int> _right) : left(_left), right(_right) {}
    
    bool apply(state const & s, int result){
        assert(left.size() == right.size());
        
        int r;
        if(left.find(s.index)!=left.end())
            r = +s.weight;
        else if(right.find(s.index)!=right.end())
            r = -s.weight;
        else
            r = 0;
        
        return r == result;
    }
    
    set<state> filter(set<state> const & ss, int r){
        set<state> result;
        for(state s: ss){
            if(apply(s, r)){
                result.insert(s);
            }
        }
        
        return result;
    }
};

struct node{
    set<state> ss;
    set<int> left;
    set<int> right;
    
    node* less;
    node* equal;
    node* greater;
    
    node() : less(nullptr), equal(nullptr), greater(nullptr) {}
    
    node(node const & other) : less(nullptr), equal(nullptr), greater(nullptr){
        ss = other.ss;
        left = other.left;
        right = other.right;
        
        if(other.less != nullptr)
            less = new node(*other.less);
        
        if(other.equal != nullptr)
            equal = new node(*other.equal);
        
        if(other.greater != nullptr)
            greater = new node(*other.greater);
    }
    
    node(set<state> const & _ss, set<int> _left, set<int> _right) : ss(_ss), left(_left), right(_right){
        test t(left, right);
        
        less = new node;
        less->ss = t.filter(ss, -1);
        
        equal = new node;
        equal->ss = t.filter(ss, 0);
        
        greater = new node;
        greater->ss = t.filter(ss, 1);
    }
    
    void apply_map(map<int,int> m){
        set<state> new_ss;
        for(auto s: ss){
            state new_state;
            new_state.index = s.index;
            new_state.weight = s.weight;
            auto itr = m.find(s.index);
            if(itr!=m.end()){
                new_state.index = itr->second;
            }
            new_ss.insert(new_state);
        }
        ss = new_ss;
        
        set<int> newleft;
        for(auto i: left){
            auto itr = m.find(i);
            if(itr!=m.end())
                newleft.insert(itr->second);
            else
                newleft.insert(i);
        }
        left = newleft;
        
        set<int> newright;
        for(auto i: right){
            auto itr = m.find(i);
            if(itr!=m.end())
                newright.insert(itr->second);
            else
                newright.insert(i);
        }
        right = newright;
        
        if(less != nullptr)
            less->apply_map(m);
        if(greater != nullptr)
            greater->apply_map(m);
        if(equal != nullptr)
            equal->apply_map(m);
    }
        
    ~node() {
        if(less != nullptr)
            delete less;
        if(equal != nullptr)
            delete equal;
        if(greater != nullptr)
            delete greater;
    }
};



bool intersect(set<int> const & s, set<int> const & t){
    for(auto i: s){
        if(t.find(i) != t.end())
            return true;
    }
    return false;
}

vector<set<int>> combinations(int n, int start, int c){
    vector<set<int>> result;
    if(c == 0){
        set<int> s;
        result.push_back(s);
        return result;
    }
    for(int i = start; i < n-(c-1); i++){
        int first = i;
        vector<set<int>> subs = combinations(n, i+1, c-1);
        for(auto s: subs){
            s.insert(first);
            result.push_back(s);
        }
    }
    return result;
}

void print(set<state> ss){
    for(auto s: ss){
        cout << s.index << (s.weight == 1 ? "+" : "-") << ",";
    }
}

void print(set<int> is){
    for(auto i: is)
        cout << i << " ";
}

void print(set<int> left, set<int> right){
    for(auto i: left)
        cout << i << " ";
    cout << ":: ";
    for(auto i: right)
        cout << i << " ";
}

void indent(int n){
    for(int i = 0; i < n; i++)
        cout << "\t";
}

void print(node const & n, int indent = 0){
    ::indent(indent);
    for(auto s: n.ss){
        cout << s.index << (s.weight == 1 ? "+" : "-") << ",";
    }
    cout << endl;
    
    if(n.ss.size()<=1)
        return;
    
    ::indent(indent);
    for(auto i: n.left)
        cout << i << " ";
    cout << ":: ";
    for(auto i: n.right)
        cout << i << " ";
    cout << endl;
    
    if(n.less != nullptr){
        ::indent(indent);
        cout << "less" << endl;
        print(*n.less, indent+1);
    }
    
    if(n.equal != nullptr){
        ::indent(indent);
        cout << "equal" << endl;
        print(*n.equal, indent+1);
    }
    
    if(n.greater != nullptr){
        ::indent(indent);
        cout << "greater" << endl;
        print(*n.greater, indent+1);
    }
}

struct canonical {
    int both, neg, pos;
    canonical(set<state> const & ss, map<int,int> & perm_map){
        set<int> p, n;
        for(auto s: ss){
            if(s.weight == -1)
                n.insert(s.index);
            else
                p.insert(s.index);
        }
        set<int> b;
        set<int> pcopy(p);
        for(auto s: pcopy){
            if(n.find(s) != n.end()){
                p.erase(s);
                n.erase(s);
                b.insert(s);
            }
        }
        
        set<int> free_indices;
        
        perm_map = map<int,int>();
        int idx = 0;
        for(auto i: b){
            if(i != idx){
                perm_map[i] = idx;
                free_indices.insert(i);
            }
            idx++;
        }
        for(auto i: p){
            if(i != idx){
                perm_map[i] = idx;
                free_indices.insert(i);
            }
            idx++;
        }
        for(auto i: n){
            if(i != idx){
                perm_map[i] = idx;
                free_indices.insert(i);
            }
            idx++;
        }
        
        
        assert(perm_map.size()==free_indices.size());
        auto freeitr = free_indices.begin();
        map<int,int> new_perm_map(perm_map);
        for(auto itr = perm_map.begin(); itr != perm_map.end(); itr++, freeitr++){
            new_perm_map[itr->second] = *freeitr;
        }
        perm_map = new_perm_map;
        
        both = (int)b.size();
        pos = (int)p.size();
        neg = (int)n.size();
    }
};

bool operator<(canonical const & first, canonical const & second)
{
    if(first.both < second.both)
        return true;
    else if(first.both > second.both)
        return false;
    else if(first.neg < second.neg)
        return true;
    else if(first.neg > second.neg)
        return false;
    else return first.pos < second.pos;
}

map<int, int> invert(map<int, int> m){
    map<int, int> result;
    for(auto k: m){
        result[k.second] = k.first;
    }
    
    return result;
}

void print(canonical const & c){
    cout << "both: " << c.both << ", pos: " << c.pos << ", neg: " << c.neg << endl;
}

map<canonical,int> failed;
map<canonical, node> cache;

node solve(int balls, set<state> ss, int steps, bool & solved){
//    indent(3-steps);
//    cout << "steps = " << steps << endl;
    if(ss.size()<=1){
        solved = true;
        node n;
        n.ss = ss;
        return n;
    }
    if(steps == 0){
        solved = false;
        node n;
        return n;
    }
    
    {
        map<int,int> perm;
        canonical canon_ss = canonical(ss, perm);
        
//        indent(3-steps);
//        print(canon_ss);
        
        {
            auto itr = failed.find(canon_ss);
            
//            if(itr != failed.end())
//                cout << "found failed entry" << endl;
            
            if(itr != failed.end() && steps <= itr->second){
//                indent(3-steps);
//                cout << "failed (cache)" << endl;
                solved = false;
                node n;
                return n;
            }
        }
        
        perm = invert(perm);
        auto itr = cache.find(canon_ss);
        if(itr != cache.end()){
//            indent(3-steps);
//            cout << "found prior!" << endl;
//            print(itr->second);
            solved = true;
            node result = itr->second;
            result.apply_map(perm);
            
            return result;
        }
    }
    
    for(int c = 1; c <= balls/2; c++){
        vector<set<int>> combs = combinations(balls, 0, c);
        for(auto l: combs) {
            bool r_rentry = false;
            for(auto r: combs) if(!intersect(l, r)){
                if(steps == 3){
                    if(r_rentry)
                        break;
                    r_rentry = true;
                    
                    indent(3-steps);
                    print(l, r);
                    cout << endl;
                }
                
                bool solvedLess, solvedEqual, solvedGreater;
                node n(ss, l, r);
                
                //            indent(3-steps);
                //            cout << "lft: ";
                //            print(n.less->ss);
                //            cout << endl;
                //            indent(3-steps);
                //            cout << "eq: ";
                //            print(n.equal->ss);
                //            cout << endl;
                //            indent(3-steps);
                //            cout << "grt: ";
                //            print(n.greater->ss);
                //            cout << endl;
                
                //            indent(3-steps);
                //            cout << "solve less" << endl;
                n.less = new node(solve(balls, n.less->ss, steps-1, solvedLess));
                if(!solvedLess) continue;
                
                //            indent(3-steps);
                //            cout << "solve equal" << endl;
                n.equal = new node(solve(balls, n.equal->ss, steps-1, solvedEqual));
                if(!solvedEqual) continue;
                
                //            indent(3-steps);
                //            cout << "solve greater" << endl;
                n.greater = new node(solve(balls, n.greater->ss, steps-1, solvedGreater));
                if(!solvedGreater) continue;
                
                solved = true;
                
                map<int,int> perm;
                canonical canon_states(ss, perm);
                node canon_node(n);
                canon_node.apply_map(perm);
                
                //            indent(3-steps);
                //            cout << "adding to cache" << endl;
                //            print(canon_node);
//                cout << "saving solution" << endl;
//                print(n);
//                print(canon_node);
                cache.insert(pair<canonical,node> {canon_states, canon_node});
                
                return n;
            }
            if(steps == 3)
                break;
        }
    }
    
    solved = false;
    {
        map<int, int> perm;
        canonical canon_states(ss, perm);
        failed.insert(pair<canonical,int> {canon_states,steps});
    }
    node n;
    return n;
}

int main()
{
    if(false){
        set<state> testss;
        testss.insert(state{9, +1});
        testss.insert(state{10, +1});
        testss.insert(state{9, -1});
        testss.insert(state{10, -1});
        testss.insert(state{0, -1});
        testss.insert(state{2, -1});
        testss.insert(state{1, +1});
        
        print(testss);
        map<int,int> perm;
        canonical canon(testss, perm);
        print(canon);
        for(auto p: perm){
            cout << p.first << " -> " << p.second << endl;
        }
        
        node n;
        n.ss = testss;
        n.apply_map(perm);
        print(n.ss);
        
        cout << "Rev" << endl;
        perm = invert(perm);
        for(auto p: perm){
            cout << p.first << " -> " << p.second << endl;
        }
        return 0;

    }
    
    set<state> ss;
    for(int i = 0; i < k; i++){
        state s = {i, +1};
        ss.insert(s);
        
        state t = {i, -1};
        ss.insert(t);
    }
    
    bool solved;
    node n = solve(k, ss, 3, solved);
    cout << "solved? " << solved << endl;
    print(n);
    return 0;
}