Find maximum possible time HH:MM by permuting four given digits

前端 未结 23 2057
执念已碎
执念已碎 2020-11-30 02:44

I recently took a coding test for a promotion at work. This was one of the tasks I really struggled with and was wondering what is the best way to do this. I used a load of

相关标签:
23条回答
  • 2020-11-30 03:03

    It's not elegant or pretty, but it seems to do the trick!

    const NOT_POSSIBLE = 'NOT POSSIBLE';
    
    function generate(A, B, C, D) {
    	var args = [A, B, C, D];
    	var idx = -1;
    	var out = NOT_POSSIBLE;
    	var firstN, secondN;
    
    	MAIN: {
    		args.sort(NUMERIC_ASCENDING);
    		// number has to start with 0, 1 or 2
    		if (args[0] > 2) break MAIN;
    
    		while (args[++idx] < 3) {}
    
    		// take the higest 2, 1, or 0
    		firstN = args[--idx];
    		args = pop(args, idx);
    
    		if (firstN === 2) {
    			// make sure that the first number doesn't exceed 23 and
    			// the second number 59
    			if (args[0] > 3 || args[0] > 1 && args[1] > 5)
    				break MAIN;
    			// advance to the first number < 3 or the length
    			idx = 0;
    			while (args[++idx] < 3){}
    		} else {
    			// much simpler if we have a 0 or 1, take the biggest n remaining
    			idx = args.length;
    		}
    
    		secondN = args[--idx];
    		args = pop(args, idx);
    		// if minutes number is too large, swap
    		if (args[0] > 5) {
    			out = '' + secondN + args[1] + ':' + firstN + args[0];
    		} else {
    			// if bottom number is low enough, swap for more minutes
    			out = '' + firstN + secondN + (args[1] < 6 ? ':' + args[1] + args[0] : ':' + args[0] + args[1]);
    		}
    	}
    	return out;
    }
    
    // numeric comparator for sort
    function NUMERIC_ASCENDING(x, y) {
    	return x > y ? 1 : y > x ? -1 : 0;
    }
    
    // specialized "array pop" I wrote out longhand that's very optimized; might be cheating =D
    function pop(arr, target) {
    	switch (arr.length) {
    	case 3:
    		switch (target) {
    		case 0: return [arr[1], arr[2]];
    		case 1: return [arr[0], arr[2]];
    		default: return [arr[0], arr[1]];
    		}
    	case 4:
    		switch (target) {
    		case 0: return [arr[1], arr[2], arr[3]];
    		case 1: return [arr[0], arr[2], arr[3]];
    		case 2: return [arr[0], arr[1], arr[3]];
    		default: return [arr[0], arr[1], arr[2]];
    		}
    	}
    }
    
    /* --------------- Start Speed Test --------------------- */
    let startTime = Math.floor(Date.now());
    let times = 10000;
    let timesHolder = times;
    
    while (times--) {
      let A = randNum();
      let B = randNum();
      let C = randNum();
      let D = randNum();
      generate(A, B, C, D);
      if (times == 0) {
        let totalTime = Math.floor(Date.now()) - startTime;
        let msg = timesHolder + ' Call Finished Within -> ' + totalTime + ' ms <-';
        console.log(msg);
      }
    }
    function randNum() {
      return Math.floor(Math.random() * (9 - 0 + 1)) + 0;
    }
    /* --------------- END Speed Test --------------------- */

    0 讨论(0)
  • 2020-11-30 03:06

    Hmmm..... I guess it's really simple if you break it into simpler problems: eg find all valid hours (00-23), for each of these valid hours use the remaining numbers to find valid minutes (00-59), combine and sort. In pseudo code something like the following

        valid_times = []
        function get_max(digits[]) {
                    for each d1 in digits[]
                for each d2 in (digits[] except d1)
                    res = is_valid_hour(d1, d2)
                    if(res > 0) {
                        if(res == 2)
                            swap(d1, d2)
                        d3 = one of the rest in (digits except d1 and d2)
                        d4 = digit left in digits[]
                        res = is_valid_minute(d3, d4)
                        if(res > 0)
                            if(res == 2)
                                swap(d3, d4)
                            add (d1, d2, d3, d4) to valid_times;
                    }
            sort(valid_times)
            print valid_times[0]
        }
    
        function is_valid_hour(a, b) {
            if (a*10+b<24)
                return 1
    
            if (b*10+a<24)
                return 2
    
            return 0;
        }
    
        function is_valid_minute(a, b) {
            if (a*10+b<60)
                return 1
    
            if (b*10+a<60)
                return 2
    
            return 0;
        }
    
    0 讨论(0)
  • 2020-11-30 03:06

    My approach is to have array of available numbers (stack) and another with return value (ret). At first I put in ret invalid values "-1". Then I sort stack descending and loop trough to try to assign biggest possible number to return stack.

    function swap(a, b, p1, p2) {
      var temp = a[p1];
      a[p1] = b[p2];
      b[p2] = temp;
    }
    
    function t(a, b, c, d) {
      var stack = [a, b, c, d];
      var ret   = [-1, -1, -1, -1];
    
      stack.sort().reverse();
      var change = true;
      var i = 0;
      // this while is assigning HOURS
      while(change === true || i < 4) {
        change = false;
        
        // Assigning at first position (Hh:mm), so number must be lower or equal to 2
        if(stack[i] <= 2 && ret[0] < stack[i]) {
          swap(ret, stack, 0, i);
          change = true;
          i = 0;
        } 
        // Assigning at second position (hH:mm), so number must be <= 4 if number 
        // at first position is 2, otherwise just make sure valid number 
        // (0 to 1) is assigned at first position
        else if(((ret[0] === 2 && stack[i] <= 4) || ret[0] < 2 && ret[0] >= 0) && ret[1] < stack[i]) {
          swap(ret, stack, 1, i);
          change = true;
          i = 0;
        }
        else i++;
      }
      
      stack.sort().reverse();
      change = true;
      i = 0;
      // This while is assigning minutes
      while(change === true || i < 4) {
        change = false;
        
        if(stack[i] <= 5 && ret[2] < stack[i]) {
          swap(ret, stack, 2, i);
          change = true;
          i = 0;
        } 
        else if(stack[i] <= 9 && ret[3] < stack[i]) {
          swap(ret, stack, 3, i);
          change = true;
          i = 0;
        }
        else i++;
      }
      
      // If return stack contains -1, invalid combination was entered
      return Math.min.apply(Math, ret) > -1
        ? ret[0] + "" + ret[1] + ":" + ret[2] + "" + ret[3]
        : "NOT POSSIBLE";
    }
    
    console.log(t(6, 5, 2, 0)); // 20:56
    console.log(t(3, 9, 5, 0)); // 09:53
    console.log(t(2, 5, 6, 8)); // NOT POSSIBLE

    0 讨论(0)
  • 2020-11-30 03:07

    With a small input and output space, using a look-up table is always an option; however, I found that in JavaScript the size of the table has a surprisingly large impact on the speed.

    If we start by sorting the input to get a canonical version, so that 4,3,2,1 and 3,1,4,2 are both transformed into 1,2,3,4, there are less than 400 possibilities that lead to a valid result. But as soon as I added more than 200 entries to the look-up table, the speed dropped considerably (which is probably browser-dependent).

    However, there are only five types of digits:

    0,1    <- can be first digit of hours followed by any digit
    2      <- can be first digit of hours followed by 0-3
    3      <- can be second digit of hours after a 2 to form 23 hours
    4,5    <- can be first digits of minutes
    6-9    <- can only be second digit of hours or minutes
    

    Within these types, the digits are interchangeable; the optimal permutation will be the same:

    2,4,0,6  ->  20:46  (ACBD)
    2,5,1,9  ->  21:59  (ACBD)
    

    If you represent the digits by digit types "0" (0-1), "2", "3", "4" (4-5), and "6" (6-9), there are only 48 combinations that lead to a valid solution, each using one of 16 different permutations. Code with these smaller look-up tables turns out to be much faster:

    function generate(A, B, C, D) {
        var swap; // sorting network
        if (A > B) { swap = A; A = B; B = swap; }
        if (C > D) { swap = C; C = D; D = swap; }
        if (A > C) { swap = A; A = C; C = swap; }
        if (B > D) { swap = B; B = D; D = swap; }
        if (B > C) { swap = B; B = C; C = swap; }
    
        var table = {"0000":15, "0002":15, "0003":14, "0004":14, "0006":14, "0022":15, 
                     "0023":14, "0024":13, "0026":12, "0033":11, "0034":11, "0036":11, 
                     "0044":11, "0046":11, "0066":10, "0222":15, "0223":14, "0224":13, 
                     "0226":12, "0233":11, "0234": 9, "0236": 8, "0244": 7, "0246": 6, 
                     "0266": 4, "0333": 5, "0334": 5, "0336": 5, "0344": 5, "0346": 5, 
                     "0366": 4, "0444": 5, "0446": 5, "0466": 4, "2222":15, "2223":14, 
                     "2224":13, "2226":12, "2233":11, "2234": 9, "2236": 8, "2244": 7, 
                     "2246": 6, "2333": 5, "2334": 3, "2336": 2, "2344": 1, "2346": 0};
    
        var type = ['0','0','2','3','4','4','6','6','6','6'];
        var key = type[A] + type[B] + type[C] + type[D];
        var permutation = table[key];
        if (permutation == undefined) return "NOT POSSIBLE";
    
        var digits = [[2,3,C,D], [2,3,D,C], [2,3,3,D], [2,3,D,3], 
                      [A,D,B,C], [A,D,C,B], [2,A,C,D], [2,A,D,C], 
                      [2,3,A,D], [2,3,D,A], [B,D,A,C], [B,D,C,A], 
                      [2,B,A,D], [2,B,D,A], [C,D,B,A], [D,C,B,A]];
    
        var time = digits[permutation];
        return "" + time[0] + time[1] + ':' + time[2] + time[3];
    }
    
    function rndDigit() { return Math.floor(Math.random() * 10); }
    for (var tests = 0; tests < 11; tests++) {
        var d = [rndDigit(), rndDigit(), rndDigit(), rndDigit()];
        document.write(d + " &rarr; " + generate(d[0],d[1],d[2],d[3]) + "<BR>");
    }

    0 讨论(0)
  • 2020-11-30 03:09

    An approach using a precomputed string, containing all possible permutations.

    function generate(A,B,C,D){
      var isValidTime = /^(?:[01]\d|2[0-3]):(?:[0-5]\d)$/;
      var pattern = "0123012 0132013 0213021 0231023 0312031 0321032".replace(/\d/g, i => arguments[+i]);
      var max = "";
      for(var i=pattern.length-4; i--; ){
        var time = pattern.substr(i,2) + ":" + pattern.substr(i+2,2);
        if(time > max && isValidTime.test(time)) 
          max = time;
      }
      return max || "NOT POSSIBLE";
    }
    
    [
      [6,5,0,2],
      [3,9,5,0],
      [7,6,3,8]
    ].forEach(arr => console.log(arr + ' -> ' + generate(...arr)));
    .as-console-wrapper{top:0;max-height:100%!important}

    but we can improve on that, by using the regex to find only valid times:

    function generate(A,B,C,D){	
      var pattern = "0123012 0132013 0213021 0231023 0312031 0321032".replace(/\d/g, i => arguments[+i]);
      console.log(pattern);
      var matchValidTime = /([01]\d|2[0-3])([0-5]\d)/g, m, max = "";
      while(m = matchValidTime.exec(pattern)){
        var time = m[1] + ":" + m[2];
        if(time > max) max = time;
        console.log("index: %o  time: %o  max: %o", m.index, time, max);
        matchValidTime.lastIndex = m.index+1; //to find intersecting matches
      }
      return max || "NOT POSSIBLE";
    }
    
       [
      [1,2,3,4],
      //[6,5,0,2],
      //[3,9,5,0],
      //[7,6,3,8]
    ].forEach(arr => console.log(arr + ' -> ' + generate(...arr)));
    .as-console-wrapper{top:0;max-height:100%!important}

    0 讨论(0)
提交回复
热议问题