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

前端 未结 23 2054
执念已碎
执念已碎 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 02:45

    This is what I came up with. Hardly elegant, I might try to tidy it make it a bit more efficient. I have a feeling a brute force approach would be the cleanest and most efficient way to do it. This is a mess.

    // w: highest value 2 or less
    // UNLESS: 1 of b, c, or d are less than 3 while the other two are greater than 7
    // x: highest value
    // UNLESS: last was 2 then highest value less than 2
    // y: highest value less than 5
    // z: highest remaining value
    
    function findhighestwhere(array, condition) {
      let res = null
      let val = -1
      let i = 0
      for (let x of array) {
        if (x !== null && condition(x) && x > val) {
          res = i
          val = x
        }
        i++
      }
      // console.log(`Test index: ${res} \n Test value: ${val}`)
      return res
    }
    
    function generate(a,b,c,d) {
      // console.log(`Testing: ${a}${b}${c}${d}`)
      let array = [a,b,c,d]
      let wi = findhighestwhere(array, x => x <= 2)
      // That one pain in the conditional edge-case
      if ( array[wi] == 2 ) {
        // console.log(`Encountered First Position 2 Checking for Edge Case`)
        let i = 0
        let lowcount = 0
        let highcount = 0
        for (let x of array) {
          if ( i != wi && x <= 3 ) lowcount++
          if ( i != wi && x >= 6 ) highcount++
          i++
        }
        if ( lowcount == 1 && highcount == 2 ) {
          // console.log(`Edge Case Encountered`)
          wi = findhighestwhere(array, x => x <= 1)
        }
      }
      if ( wi === null ) return false
      let w = array[wi]
      // console.log(`W: ${w}`)
      array[wi] = null  
      if ( w == 2 ) {
        var xi = findhighestwhere(array, x => x <= 3)
      } else {
        var xi = findhighestwhere(array, x => true)
      }
      if ( xi === null ) return false
      let x = array[xi]
      // console.log(`X: ${x}`)
      array[xi] = null
      let yi = findhighestwhere(array, x => x <= 5)
      if ( yi === null ) return false
      let y = array[yi]
      // console.log(`Y: ${y}`)
      array[yi] = null
      let zi = findhighestwhere(array, x => true)
      if ( zi === null ) return false
      let z = array[zi]
      // console.log(`Z: ${z}`)
      array[zi] = null
    
      return `${w}${x}:${y}${z}`
    }
    
    
    console.log(`6520: ${generate(6,5,2,0)}`) // 6520: 20:56
    console.log(`3950: ${generate(3,9,5,0)}`) // 3950: 09:53
    console.log(`7638: ${generate(7,6,3,8)}`) // 7638: false
    console.log(`1727: ${generate(1,7,2,7)}`) // 1727: 17:27
    
    0 讨论(0)
  • 2020-11-30 02:46

    I think this method is called Brute Force. Took test samples from @Dummy's answer.

    <script>
    function generate(A, B, C, D) {
        var result = -1
        var v = [A, B, C, D]
        for (i = 0; i < 4; i++) {
            for (j = 0; j < 4; j++) if (j != i) {
                for (k = 0; k < 4; k++) if (k != j && k != i) {
                    for (m = 0; m < 4; m++) if (m != k && m != j && m != i) {
                        if (v[i]*10 + v[j] < 24 && v[k]*10 + v[m] < 60) { //legal time
                            if (v[i]*1000 + v[j]*100 + v[k]*10 + v[m] > result) {
                                result = v[i]*1000 + v[j]*100 + v[k]*10 + v[m]
                            }
                        }
                    }
                }
            }
        }
        return result >= 0? Math.floor(result/100) + ':' + result%100: 'NOT POSSIBLE'
    } 
    
    console.log('generate(1,7,2,7)', generate(1,7,2,7))
    console.log('generate(6,5,2,0)', generate(6,5,2,0))
    console.log('generate(3,9,5,0)', generate(3,9,5,0))
    console.log('generate(7,6,3,8)', generate(7,6,3,8))
    console.log('generate(0,1,2,3)', generate(0,1,2,3))
    console.log('generate(1,1,1,2)', generate(1,1,1,2))
    console.log('generate(1,1,1,1)', generate(1,1,1,1))
    console.log('generate(5,6,7,8)', generate(5,6,7,8))
    console.log('generate(2,9,3,1)', generate(2,9,3,1))
    </script>
    
    0 讨论(0)
  • 2020-11-30 02:48

    Here is a non brute force solution that I came up with. Check out the comments in the code to see how it works. If any of it is unclear I can help clarify.

    function generate(A, B, C, D) {
        vals = [A, B, C, D];
        counts = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        for (i = 0; i < vals.length; i++) {
            for (j = vals[i]; j < counts.length; j++) counts[j]++;
        }
        // counts is now populated with the number of values less than or equal to the index it belongs to
        // so counts[2] is the total number of 0's, 1's and 2's
        if (counts[2] === 0) return 'NOT POSSIBLE';
        // if there are no 0's and 1's, then it must start with 2
        mustStartWith2 = counts[1] === 0;
        if (mustStartWith2 && counts[3] === 1) return 'NOT POSSIBLE';
        // We want a count of the number of free digits that are 5 or less (for the minute digit)
        numbersAvailableForMinute = counts[5] - (mustStartWith2 ? 2 : 1); 
        if (numbersAvailableForMinute === 0) return 'NOT POSSIBLE';
        // we now know that it is a valid time
        time = [0, 0, 0, 0];
        // we also know if it starts with 2
        startsWith2 = mustStartWith2 || (numbersAvailableForMinute >= 2 && counts[2] > counts[1]);
        // knowing the starting digit, we know the maximum value for each digit
        maxs = startsWith2 ? [2, 3, 5, 9] : [1, 9, 5, 9];
        for (i = 0; i < maxs.length; i++) {
            // find the first occurrence in counts that has the same count as the maximum
            time[i] = counts.indexOf(counts[maxs[i]]);
            // update counts after the value was removed
            for (j = time[i]; j < counts.length; j++) counts[j]--;
        }
        // create the time
        return time[0]+""+time[1]+":"+time[2]+""+time[3];
    }
    
    0 讨论(0)
  • 2020-11-30 02:48

    Added executable snippet and some test cases

    function generate(A, B, C, D) {
      var combinations = []
      arguments = Array.from(arguments)
      for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
          if (i !== j) {
            var num = +(arguments[i] + '' + arguments[j])
            if (num <= 59 && combinations.indexOf(num) === -1)
              combinations.push(num)
          }
        }
      }
      combinations.sort((a, b) => a - b);
      var hours = combinations.filter(hour => hour <= 23);
    
      for (var i = hours.length - 1; i >= 0; i--) {
        for (var j = combinations.length - 1; j >= 0; j--) {
          if (computeMax(hours[i], combinations[j], arguments))
            return hours[i] + ':' + combinations[j]
        }
      }
      return 'not possible'
    }
    
    function computeMax(maxHour, maxMinute, args) {
      var minute = String(maxMinute)
      var hour = String(maxHour)
      for (var k = 0; k < minute.length; k++)
        if (hour.indexOf(minute[k]) > -1 && args.indexOf(+minute[k]) === args.lastIndexOf(+minute[k]))
          return false
      return true
    }
    console.log('generate(1,7,2,7)', generate(1,7,2,7))
    console.log('generate(6,5,2,0)', generate(6,5,2,0))
    console.log('generate(3,9,5,0)', generate(3,9,5,0))
    console.log('generate(7,6,3,8)', generate(7,6,3,8))
    console.log('generate(0,1,2,3)', generate(0,1,2,3))
    console.log('generate(1,1,1,2)', generate(1,1,1,2))
    console.log('generate(1,1,1,1)', generate(1,1,1,1))
    console.log('generate(5,6,7,8)', generate(5,6,7,8))
    console.log('generate(2,9,3,1)', generate(2,9,3,1))

    0 讨论(0)
  • 2020-11-30 02:48

    UPDATED

    Just try to find some way to improve the performance, the new idea is inspired by counting sort.

    Simply count the number of each digit, then base on the following chain of dependencies, brute find the optimal possibilities. The answer would be one of those, highest priority first:

    1. 2[max digit <= 3]:[max digit <= 5][*]
    2. 1[*]:[max digit <= 5][*]
    3. 0[*]:[max digit <= 5][*]

    /* --------------- Start Speed Test --------------------- */
      var startTime = Math.floor(Date.now());
      var times = 10000; //how many generate call you want?
      var timesHolder = times;
    
      while (times--) {
        var A = randNum();
        var B = randNum();
        var C = randNum();
        var D = randNum();
        generate(A, B, C, D);
        if (times == 0) {
          var totalTime = Math.floor(Date.now()) - startTime;
          var msg = timesHolder + ' Call Finished Within -> ' + totalTime + ' ms <-';
          console.log(msg);
          alert(msg);
        }
      }
    
      function randNum() {
        return Math.floor(Math.random() * (9 - 0 + 1)) + 0;
      }
      /* --------------- END Speed Test --------------------- */
      
      function generate(A,B,C,D){
          var cnt = [0,0,0,0,0,0,0,0,0,0], ans = ['', ''];      
          cnt[A]++; cnt[B]++; cnt[C]++; cnt[D]++;
          
          function gen(part, max){
             for(var i=max; i>=0; i--) if(cnt[i]){
                  ans[part] += i;
                  cnt[i]--;
                  return 1;
              }
              return 0;
          }
          function rollback(first){
              cnt[first]++;
              for(var i in ans[0]) cnt[ans[0][i]]++;
              for(var i in ans[1]) cnt[ans[1][i]]++;
              ans[0] = ans[1] = '';
          }
          /*** Main logic, based on the chain of dependencies ***/
          if(cnt[2]){
              cnt[2]--;
              if(!gen(0, 3) || !gen(1,5) || !gen(1,9)) rollback(2);
              else return '2' + ans[0] + ':' + ans[1];
          }
          if(cnt[1]){
              cnt[1]--;
              if(!gen(0, 9) || !gen(1,5) || !gen(1,9)) rollback(1);
              else return '1' + ans[0] + ':' + ans[1];
          }
          if(cnt[0]){
              cnt[0]--;
              if(!gen(0, 9) || !gen(1,5) || !gen(1,9)) rollback(0);
              else return '0' + ans[0] + ':' + ans[1];
          }
          return 'NOT POSSIBLE';
      }
      console.log(generate(1,7,2,7));
      console.log(generate(0,0,2,9));
      console.log(generate(6,5,2,0));
      console.log(generate(3,9,5,0));
      console.log(generate(7,6,3,8));
      console.log(generate(0,0,0,0));
      console.log(generate(9,9,9,9));
      console.log(generate(1,2,3,4));

    0 讨论(0)
  • 2020-11-30 02:49

    I recently was working on the same problem (with 6 digits though) and came up with this non-brute solution:

      #include <iostream>
      #include <iomanip>
    
      int numbers[6] = { 0, 0, 0, 0, 0, 0 };
      int input[6] = { 1, 7, 3, 3, 4, 1 };
    
      void buildHistogram() {
          for (int i = 0; i < 6; ++i) {
              numbers[input[i]]++;
          }
      }
    
      int getMaxNotExceeding(int number) {
          for (int i = number; i >= 0; --i) {
              if (numbers[i] > 0) {
                  numbers[i]--;
                  return i;
              }
          }
          throw std::exception("CANNOT CREATE TIME");
      }
    
      int main() {
          try {
              buildHistogram();
              int hours = (getMaxNotExceeding(2) * 10);
              if (hours < 20) {
                hours += getMaxNotExceeding(9);
              } else {
                hours += getMaxNotExceeding(3);
              }
              int minutes = (getMaxNotExceeding(5) * 10) + getMaxNotExceeding(9);
              int seconds = (getMaxNotExceeding(5) * 10) + getMaxNotExceeding(9);
    
              if (seconds > 59 || minutes > 59 || hours > 23) {
                  throw std::exception("CANNOT CREATE TIME");
              }
              std::cout.fill('0');
              std::cout << std::setw(2) << hours << ':' << std::setw(2) << minutes << ':' << std::setw(2) << seconds << std::endl;
          } catch(const std::exception& ex) {
              std::cout << ex.what() << std::endl;
          }
          return 0;
      }
    
    0 讨论(0)
提交回复
热议问题