Skip to content

Instantly share code, notes, and snippets.

@Sharlottes
Created May 30, 2023 03:57
Show Gist options
  • Save Sharlottes/09965d95d8e27e682726bf39311cc4e6 to your computer and use it in GitHub Desktop.
Save Sharlottes/09965d95d8e27e682726bf39311cc4e6 to your computer and use it in GitHub Desktop.
https://www.acmicpc.net/problem/27434 문제를 자바스크립트로 통과한 코드
function factorialByLoop(number) {
let num = 1n;
for (let i = 0; i < number; i++) {
num *= BigInt(number - i);
}
return num;
}
function factorialByReversedArray(number) {
const arr = new Array();
for (let i = number; i >= 0; i--) arr.push(BigInt(i == 0 ? 1 : i));
while (arr.length > 1) {
arr.push(arr.pop() * arr.pop());
}
return arr.pop();
}
function factorialByArray(number) {
const arr = new Array();
for (let i = 0; i <= number; i++) arr.push(BigInt(i == 0 ? 1 : i));
while (arr.length > 1) {
arr.push(arr.pop() * arr.pop());
}
return arr.pop();
}
function factorialByMinHeap(number) {
const heap = new MinHeap();
for (let i = 0; i <= number; i++) heap.insert(BigInt(i == 0 ? 1 : i));
while (heap.heap.length > 1) {
heap.insert(heap.remove() * heap.remove());
}
return heap.remove();
}
function factorialByMaxHeap(number) {
const heap = new MaxHeap();
for (let i = 0; i <= number; i++) heap.insert(BigInt(i == 0 ? 1 : i));
while (heap.heap.length > 1) {
heap.insert(heap.remove() * heap.remove());
}
return heap.remove();
}
function factorialByLoopWithOptionalBigInt(number) {
let num = 1;
for (let i = 1; i <= number; i++) {
num =
typeof num === "bigint"
? num * BigInt(i)
: num * i < Math.MAX_SAFE_INTEGER
? num * i
: BigInt(num) * BigInt(i);
}
return num;
}
function factorialByReversedArrayWithOptionalBigInt(number) {
const arr = new Array();
for (let i = number; i > 0; i--) arr.push(i);
arr.push(1);
while (arr.length > 1) {
const a = arr.pop();
const b = arr.pop();
const a1 = typeof a === "bigint" || typeof b !== "bigint" ? a : BigInt(a);
const b1 = typeof b === "bigint" || typeof a !== "bigint" ? b : BigInt(b);
const res = a1 * b1;
if (typeof res == "bigint") arr.push(res);
else if (res < Number.MAX_SAFE_INTEGER) arr.push(res);
else arr.push(BigInt(a1) * BigInt(b1));
}
return arr.pop();
}
function factorialByArrayWithOptionalBigInt(number) {
const arr = new Array();
arr.push(1);
for (let i = 0; i <= number; i++) arr.push(i);
while (arr.length > 1) {
const a = arr.pop();
const b = arr.pop();
const a1 = typeof a === "bigint" || typeof b !== "bigint" ? a : BigInt(a);
const b1 = typeof b === "bigint" || typeof a !== "bigint" ? b : BigInt(b);
const res = a1 * b1;
if (typeof res == "bigint") arr.push(res);
else if (res < Number.MAX_SAFE_INTEGER) arr.push(res);
else arr.push(BigInt(a1) * BigInt(b1));
}
return arr.pop();
}
function factorialByMinHeapWithOptionalBigInt(number) {
const heap = new MinHeap();
heap.insert(1);
for (let i = 1; i <= number; i++) heap.insert(i);
while (heap.heap.length > 1) {
const a = heap.remove();
const b = heap.remove();
const a1 = typeof a === "bigint" || typeof b !== "bigint" ? a : BigInt(a);
const b1 = typeof b === "bigint" || typeof a !== "bigint" ? b : BigInt(b);
const res = a1 * b1;
if (typeof res == "bigint") heap.insert(res);
else if (res < Number.MAX_SAFE_INTEGER) heap.insert(res);
else heap.insert(BigInt(a1) * BigInt(b1));
}
return heap.remove();
}
function factorialByMaxHeapWithOptionalBigInt(number) {
const heap = new MaxHeap();
heap.insert(1);
for (let i = 1; i <= number; i++) heap.insert(i);
while (heap.heap.length > 1) {
const a = heap.remove();
const b = heap.remove();
const a1 = typeof a === "bigint" || typeof b !== "bigint" ? a : BigInt(a);
const b1 = typeof b === "bigint" || typeof a !== "bigint" ? b : BigInt(b);
const res = a1 * b1;
if (typeof res == "bigint") heap.insert(res);
else if (res < Number.MAX_SAFE_INTEGER) heap.insert(res);
else heap.insert(BigInt(a1) * BigInt(b1));
}
return heap.remove();
}
class MinHeap {
/** @type Array<{ data: number }> */
heap = [];
getLeftChildIndex = (parentIndex) => parentIndex * 2 + 1;
getRightChildIndex = (parentIndex) => parentIndex * 2 + 2;
getParentIndex = (childIndex) => Math.floor((childIndex - 1) / 2);
peek = () => this.heap[0];
insert = (data) => {
const node = { data };
this.heap.push(node);
this.heapifyUp(); // 배열에 가장 끝에 넣고, 다시 min heap 의 형태를 갖추도록 한다.
};
// 최근에 삽입된 노드가 제 자리를 찾아갈 수 있도록 하는 메소드
heapifyUp = () => {
let index = this.heap.length - 1; // 계속해서 변하는 index 값
const lastInsertedNode = this.heap[index];
// 루트노드가 되기 전까지
while (index > 0) {
const parentIndex = this.getParentIndex(index);
// 부모 노드의 data 값이 마지막에 삽입된 노드의 키 값 보다 크다면
// 부모의 자리를 계속해서 아래로 내린다.
if (this.heap[parentIndex].data > lastInsertedNode.data) {
this.heap[index] = this.heap[parentIndex];
index = parentIndex;
} else break;
}
// break 를 만나서 자신의 자리를 찾은 상황
// 마지막에 찾아진 곳이 가장 나중에 들어온 노드가 들어갈 자리다.
this.heap[index] = lastInsertedNode;
};
remove = () => {
const count = this.heap.length;
const rootNode = this.heap[0];
if (count <= 0) return undefined;
if (count === 1) this.heap = [];
else {
this.heap[0] = this.heap.pop(); // 끝에 있는 노드를 부모로 만들고
this.heapifyDown(); // 다시 min heap 의 형태를 갖추도록 한다.
}
return rootNode.data;
};
// 변경된 루트노드가 제 자리를 찾아가도록 하는 메소드
heapifyDown = () => {
let index = 0;
const count = this.heap.length;
const rootNode = this.heap[index];
// 계속해서 left child 가 있을 때 까지 검사한다.
while (this.getLeftChildIndex(index) < count) {
const leftChildIndex = this.getLeftChildIndex(index);
const rightChildIndex = this.getRightChildIndex(index);
// 왼쪽, 오른쪽 중에 더 작은 노드를 찾는다
// rightChild 가 있다면 data의 값을 비교해서 더 작은 값을 찾는다.
// 없다면 leftChild 가 더 작은 값을 가지는 인덱스가 된다.
const smallerChildIndex =
rightChildIndex < count &&
this.heap[rightChildIndex].data < this.heap[leftChildIndex].data
? rightChildIndex
: leftChildIndex;
// 자식 노드의 키 값이 루트노드보다 작다면 위로 끌어올린다.
if (this.heap[smallerChildIndex].data <= rootNode.data) {
this.heap[index] = this.heap[smallerChildIndex];
index = smallerChildIndex;
} else break;
}
this.heap[index] = rootNode;
};
}
class MaxHeap {
/** @type Array<{ data: number }> */
heap = [];
getLeftChildIndex = (parentIndex) => parentIndex * 2 + 1;
getRightChildIndex = (parentIndex) => parentIndex * 2 + 2;
getParentIndex = (childIndex) => Math.floor((childIndex - 1) / 2);
peek = () => this.heap[0];
insert = (data) => {
const node = { data };
this.heap.push(node);
this.heapifyUp(); // 배열에 가장 끝에 넣고, 다시 max heap 의 형태를 갖추도록 한다.
};
// 최근에 삽입된 노드가 제 자리를 찾아갈 수 있도록 하는 메소드
heapifyUp = () => {
let index = this.heap.length - 1; // 계속해서 변하는 index 값
const lastInsertedNode = this.heap[index];
// 루트노드가 되기 전까지
while (index > 0) {
const parentIndex = this.getParentIndex(index);
// 부모 노드의 data 값이 마지막에 삽입된 노드의 키 값 보다 작다면
// 부모의 자리를 계속해서 아래로 내린다.
if (this.heap[parentIndex].data < lastInsertedNode.data) {
this.heap[index] = this.heap[parentIndex];
index = parentIndex;
} else break;
}
// break 를 만나서 자신의 자리를 찾은 상황
// 마지막에 찾아진 곳이 가장 나중에 들어온 노드가 들어갈 자리다.
this.heap[index] = lastInsertedNode;
};
remove = () => {
const count = this.heap.length;
const rootNode = this.heap[0];
if (count <= 0) return undefined;
if (count === 1) this.heap = [];
else {
this.heap[0] = this.heap.pop(); // 끝에 있는 노드를 부모로 만들고
this.heapifyDown(); // 다시 max heap 의 형태를 갖추도록 한다.
}
return rootNode.data;
};
// 변경된 루트노드가 제 자리를 찾아가도록 하는 메소드
heapifyDown = () => {
let index = 0;
const count = this.heap.length;
const rootNode = this.heap[index];
// 계속해서 left child 가 있을 때 까지 검사한다.
while (this.getLeftChildIndex(index) < count) {
const leftChildIndex = this.getLeftChildIndex(index);
const rightChildIndex = this.getRightChildIndex(index);
// 왼쪽, 오른쪽 중에 더 큰 노드를 찾는다
// rightChild 가 있다면 data의 값을 비교해서 더 큰 값을 찾는다.
// 없다면 leftChild 가 더 큰 값을 가지는 인덱스가 된다.
const smallerChildIndex =
rightChildIndex < count &&
this.heap[rightChildIndex].data > this.heap[leftChildIndex].data
? rightChildIndex
: leftChildIndex;
// 자식 노드의 키 값이 루트노드보다 크다면 위로 끌어올린다.
if (this.heap[smallerChildIndex].data >= rootNode.data) {
this.heap[index] = this.heap[smallerChildIndex];
index = smallerChildIndex;
} else break;
}
this.heap[index] = rootNode;
};
}
const lut = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800];
function factorialMix(number) {
if (number <= 10) return lut[number];
else if (number < 500) return factorialByLoopWithOptionalBigInt(number);
else if (number < 1000) return factorialByReversedArray(number);
else if (number < 10000)
return factorialByReversedArrayWithOptionalBigInt(number);
else return factorialByMinHeapWithOptionalBigInt(number);
}
const rl = require("readline").createInterface(process.stdin, process.stdout);
rl.on("line", (line) => {
console.log(factorialMix(+line).toString());
rl.close();
});
@Sharlottes
Copy link
Author

위에껀 극한의 속도 개선을 위해 온갖 똥꼬쇼를 저지른 결과.
아래는 첫 통과 코드. 핵심은 PQ를 동원하기

const rl = require("readline").createInterface(process.stdin, process.stdout);
rl.on("line", (line) => {
  console.log(factorial(+line).toString());
  rl.close();
});

function factorial(number) {
  const heap = new PriorityQueue();
  for (let i = 0; i <= number; i++) heap.enqueue(BigInt(i == 0 ? 1 : i));
  while (heap.heap.length > 1) {
    heap.enqueue(heap.dequeue().data * heap.dequeue().data);
  }
  return heap.dequeue().data;
}

class Heap {
  /** @type Array<{ data: number; }> */
  heap = [];

  getLeftChildIndex = (parentIndex) => parentIndex * 2 + 1;
  getRightChildIndex = (parentIndex) => parentIndex * 2 + 2;
  getParentIndex = (childIndex) => Math.floor((childIndex - 1) / 2);

  peek = () => this.heap[0];

  insert(data) {
    const node = { data };
    this.heap.push(node);
    this.heapifyUp();
  }

  remove() {
    const count = this.heap.length;
    const rootNode = this.peek();

    if (count <= 0) return undefined;
    if (count === 1) this.heap = [];
    else {
      this.heap[0] = this.heap.pop();
      this.heapifyDown();
    }

    return rootNode;
  }

  heapifyUp() {
    let currentIndex = this.heap.length - 1;
    const lastInsertedNode = this.heap[currentIndex];

    while (currentIndex > 0) {
      const parentIndex = this.getParentIndex(currentIndex);

      if (this.heap[parentIndex].data > lastInsertedNode.data) {
        this.heap[currentIndex] = this.heap[parentIndex];
        currentIndex = parentIndex;
      } else break;
    }

    this.heap[currentIndex] = lastInsertedNode;
  }

  heapifyDown() {
    let index = 0;
    const count = this.heap.length;
    const rootNode = this.peek();

    while (this.getLeftChildIndex(index) < count) {
      const leftChildIndex = this.getLeftChildIndex(index);
      const rightChildIndex = this.getRightChildIndex(index);
      const smallerChildIndex =
        rightChildIndex < count &&
        this.heap[rightChildIndex].data < this.heap[leftChildIndex].data
          ? rightChildIndex
          : leftChildIndex;

      if (this.heap[smallerChildIndex].data <= rootNode.data) {
        this.heap[index] = this.heap[smallerChildIndex];
        index = smallerChildIndex;
      } else break;
    }

    this.heap[index] = rootNode;
  }
}

class PriorityQueue extends Heap {
  enqueue = (value) => this.insert(value);
  dequeue = () => this.remove();
  isEmpty = () => this.heap.length <= 0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment