🏠 Home 

Multiset

Multiset data structure

Этот скрипт недоступен для установки пользователем. Он является библиотекой, которая подключается к другим скриптам мета-ключом // @require https://update.greasyfork.org/scripts/526316/1534016/Multiset.js

// ==UserScript==
// @name        Multiset
// @namespace   Data Structures
// @grant       none
// @version     0.0.1
// @author      Gemini 2.0
// @description Multiset data structure
// ==/UserScript==
// Changelog
// 0.0.1 - January 8, 2025
// - Initial release
const Multiset = (function() {
'use strict';
// LLM: Multiset written by Gemini 2.0 Flash Thinking Experimental 01-21.
/**
* Represents a Multiset (or Bag) data structure.
* A multiset is like a set, but it allows duplicate elements.
*/
class Multiset {
constructor(iterable = []) {
/**
* Internal storage for the multiset.
* Uses a Map where keys are elements and values are their counts.
* @private
* @type {Map<any, number>}
*/
this._counts = new Map();
/**
* The total number of elements in the multiset (including duplicates).
* @private
* @type {number}
*/
this._size = 0;
// Initialize from an iterable if provided
for (const element of iterable) {
this.add(element);
}
}
/**
* Adds an element to the multiset.
* If the element already exists, increments its count.
* @param {any} element The element to add.
* @param {number} [count=1] The number of times to add the element.
* @returns {this} The Multiset instance (for chaining).
*/
add(element, count = 1) {
if (typeof count !== 'number') {
throw new TypeError("Count must be a number.");
}
if (count <= 0) {
return this; // No-op for non-positive counts
}
const currentCount = this._counts.get(element) || 0;
this._counts.set(element, currentCount + count);
this._size += count;
return this;
}
/**
* Deletes a specific element from the multiset.
* If the element exists, decrements its count.
* If the count becomes zero after deletion, the element is removed entirely.
* @param {any} element The element to delete.
* @param {number} [count=1] The number of times to delete the element.
* @returns {boolean} True if the element was present (and potentially deleted), false otherwise.
*/
delete(element, count = 1) {
if (typeof count !== 'number') {
throw new TypeError("Count must be a number.");
}
if (count <= 0) {
return false; // No-op for non-positive counts
}
if (!this.has(element)) {
return false; // Element not present
}
const currentCount = this._counts.get(element);
if (count >= currentCount) {
this._counts.delete(element);
this._size -= currentCount;
} else {
this._counts.set(element, currentCount - count);
this._size -= count;
}
return true;
}
/**
* Checks if the multiset contains a specific element (at least once).
* @param {any} element The element to check for.
* @returns {boolean} True if the element is in the multiset, false otherwise.
*/
has(element) {
return this._counts.has(element);
}
/**
* Returns the count of a specific element in the multiset.
* @param {any} element The element to get the count for.
* @returns {number} The count of the element. Returns 0 if the element is not in the multiset.
*/
count(element) {
return this._counts.get(element) || 0;
}
/**
* Clears the multiset, removing all elements.
* @returns {void}
*/
clear() {
this._counts.clear();
this._size = 0;
}
/**
* Returns the total number of elements in the multiset (including duplicates).
* @returns {number} The size of the multiset.
*/
get size() {
return this._size;
}
/**
* Returns an array representation of the multiset.
* The array will contain each element as many times as it appears in the multiset.
* The order of elements in the array is not guaranteed to be specific.
* @returns {any[]} An array of elements in the multiset.
*/
toArray() {
const r###lt = [];
for (const [element, count] of this._counts.entries()) {
for (let i = 0; i < count; i++) {
r###lt.push(element);
}
}
return r###lt;
}
/**
* Executes a provided function once for each element in the multiset.
* The function is called with the element and the multiset itself as arguments.
* Elements are iterated over as many times as they appear in the multiset.
* @param {function(any, Multiset): void} callbackFn The function to execute for each element.
* @param {any} [thisArg] Value to use as `this` when executing `callbackFn`.
* @returns {void}
*/
forEach(callbackFn, thisArg) {
for (const [element, count] of this._counts.entries()) {
for (let i = 0; i < count; i++) {
callbackFn.call(thisArg, element, this);
}
}
}
/**
* Returns an iterator that yields each element in the multiset, respecting duplicates.
* Elements are iterated over as many times as they appear in the multiset.
* The order of elements is not guaranteed to be specific.
* @returns {Iterator<any>} An iterator for the multiset elements.
*/
*[Symbol.iterator]() {
for (const [element, count] of this._counts.entries()) {
for (let i = 0; i < count; i++) {
yield element;
}
}
}
/**
* Returns an iterator that yields [element, count] pairs for each unique element in the multiset.
* @returns {Iterator<[any, number]>} An iterator for the multiset entries.
*/
entries() {
return this._counts.entries();
}
/**
* Returns an iterator that yields the unique elements in the multiset (keys).
* @returns {Iterator<any>} An iterator for the unique elements (keys).
*/
keys() {
return this._counts.keys();
}
/**
* Returns an iterator that yields the counts of each unique element in the multiset (values).
* @returns {Iterator<number>} An iterator for the element counts (values).
*/
values() {
return this._counts.values();
}
/**
* Checks if another Multiset is equal to this Multiset.
* Two multisets are equal if they contain the same elements with the same counts.
* @param {Multiset} otherMultiset The Multiset to compare with.
* @returns {boolean} True if the multisets are equal, false otherwise.
*/
equals(otherMultiset) {
if (!(otherMultiset instanceof Multiset)) {
return false;
}
if (this.size !== otherMultiset.size) {
return false;
}
if (this._counts.size !== otherMultiset._counts.size) {
return false; // Different number of unique elements
}
for (const [element, count] of this._counts.entries()) {
if (otherMultiset.count(element) !== count) {
return false;
}
}
return true;
}
/**
* Returns a new Multiset containing elements present in this multiset but not in the other multiset.
* For elements present in both, the count in the r###lting multiset is the count in this multiset minus the count in the other multiset, or zero if the count in the other multiset is greater or equal.
* @param {Multiset} otherMultiset The other multiset to compute the difference with.
* @returns {Multiset} A new Multiset representing the difference.
*/
difference(otherMultiset) {
const r###ltMultiset = new Multiset();
for (const [element, count] of this._counts.entries()) {
const otherCount = otherMultiset.count(element);
const diffCount = Math.max(0, count - otherCount); // Ensure count is not negative
if (diffCount > 0) {
r###ltMultiset.add(element, diffCount);
}
}
return r###ltMultiset;
}
/**
* Returns a new Multiset containing elements present in both this multiset and the other multiset.
* For elements present in both, the count in the r###lting multiset is the minimum of their counts in both multisets.
* @param {Multiset} otherMultiset The other multiset to compute the intersection with.
* @returns {Multiset} A new Multiset representing the intersection.
*/
intersection(otherMultiset) {
const r###ltMultiset = new Multiset();
for (const [element, count] of this._counts.entries()) {
const otherCount = otherMultiset.count(element);
const intersectionCount = Math.min(count, otherCount);
if (intersectionCount > 0) {
r###ltMultiset.add(element, intersectionCount);
}
}
return r###ltMultiset;
}
/**
* Checks if this multiset is disjoint from another multiset.
* Two multisets are disjoint if they have no elements in common (their intersection is empty).
* @param {Multiset} otherMultiset The other multiset to check for disjointness.
* @returns {boolean} True if the multisets are disjoint, false otherwise.
*/
isDisjointFrom(otherMultiset) {
return this.intersection(otherMultiset).size === 0;
}
/**
* Checks if this multiset is a subset of another multiset.
* This multiset is a subset of the other multiset if for every element in this multiset, its count is less than or equal to its count in the other multiset.
* @param {Multiset} otherMultiset The other multiset to check if this multiset is a subset of.
* @returns {boolean} True if this multiset is a subset of the other multiset, false otherwise.
*/
isSubsetOf(otherMultiset) {
for (const [element, count] of this._counts.entries()) {
if (otherMultiset.count(element) < count) {
return false;
}
}
return true;
}
/**
* Checks if this multiset is a superset of another multiset.
* This multiset is a superset of the other multiset if for every element in the other multiset, its count is less than or equal to its count in this multiset.
* @param {Multiset} otherMultiset The other multiset to check if this multiset is a superset of.
* @returns {boolean} True if this multiset is a superset of the other multiset, false otherwise.
*/
isSupersetOf(otherMultiset) {
return otherMultiset.isSubsetOf(this); // Reusing isSubsetOf for efficiency
}
/**
* Returns a new Multiset containing all elements from both this multiset and the other multiset.
* For elements present in both, the count in the r###lting multiset is the maximum of their counts in both multisets.
* Note: This is often referred to as "set-like" union for multisets. For "disjoint" union, you would sum the counts instead.
* @param {Multiset} otherMultiset The other multiset to compute the union with.
* @returns {Multiset} A new Multiset representing the union.
*/
setUnion(otherMultiset) {
const r###ltMultiset = new Multiset();
// Add all elements from this multiset
for (const [element, count] of this._counts.entries()) {
r###ltMultiset.add(element, count);
}
// Add elements from the other multiset, taking the maximum count if already present
for (const [element, otherCount] of otherMultiset._counts.entries()) {
const currentCount = r###ltMultiset.count(element);
r###ltMultiset.add(element, Math.max(0, otherCount - currentCount)); // Add the difference to reach the max count
}
return r###ltMultiset;
}
// HUMAN: Modified from setUnion to use sum of counts from both multisets instead of maximum.
disjointUnion(otherMultiset) {
const r###ltMultiset = new Multiset();
// Add all elements from this multiset
for (const [element, count] of this._counts.entries()) {
r###ltMultiset.add(element, count);
}
// Add elements from the other multiset, summing the counts
for (const [element, otherCount] of otherMultiset._counts.entries()) {
const currentCount = r###ltMultiset.count(element);
r###ltMultiset.add(element, otherCount);
}
return r###ltMultiset;
}
/**
* Returns a new Multiset containing elements that are in either this multiset or the other multiset, but not in both.
* For elements present in both, the count in the r###lting multiset is the absolute difference of their counts in the two multisets.
* @param {Multiset} otherMultiset The other multiset to compute the symmetric difference with.
* @returns {Multiset} A new Multiset representing the symmetric difference.
*/
symmetricDifference(otherMultiset) {
const r###ltMultiset = new Multiset();
const allKeys = new Set([...this.keys(), ...otherMultiset.keys()]); // Unique keys from both multisets
for (const element of allKeys) {
const count1 = this.count(element);
const count2 = otherMultiset.count(element);
const diffCount = Math.abs(count1 - count2);
if (diffCount > 0) {
r###ltMultiset.add(element, diffCount);
}
}
return r###ltMultiset;
}
}
function main() {
}
main();
return Multiset;
})();