RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 778359
Accepted
diralik
diralik
Asked:2020-02-01 02:47:10 +0000 UTC2020-02-01 02:47:10 +0000 UTC 2020-02-01 02:47:10 +0000 UTC

多个数组的笛卡尔积

  • 772

如何在 JavaScript 中实现多个数组的笛卡尔积?

// например
cartesian([1,2], [10,20], [100,200,300])
// будет равно
// [[1, 10, 100], [1, 10, 200], [1, 10, 300], [1, 20, 100], [1, 20, 200], ...]
javascript
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. Best Answer
    diralik
    2020-02-01T02:47:10Z2020-02-01T02:47:10Z

    纯 JavaScript 中的最短路径

    const cartesian =
      (...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [...d, e])), [[]]);
    

    此方法正确处理空笛卡尔积,并且在数组中有嵌套数组时也可以正常工作(与 enSO 的答案不同!)。

    被使用:

    • 数组的方法.reduce, .map,.flatMap
    • 扩展运算符...和
    • 剩余参数语法

    与评论相同。

    const cartesian = (...arrays) =>
        // итеративно получаем декартово произведение
        // нескольких первых массивов из arrays,
        // начиная с нуля массивов и пустого декартова произведения --- [[]]
        arrays.reduce((cartesianPart, array) =>
            // cartesianPart --- декартово произведение нескольких первых массивов из arrays
            cartesianPart.flatMap(cartesianPartTuple =>
                // array --- новый массив из arrays для добавления в декартово произведение
                array.map(arrayElement =>
                    // cartesianPartTuple --- массив-префикс одного из элементов декартова произведения
                    // arrayElement --- элемент одного из массива из arrays
                    [...cartesianPartTuple, arrayElement]
                )
            ),
            [[]]
        );
    

    非递归方法,通过其数显式构造笛卡尔积的元素

    function cartesian(...arrays) {
        // находим число элементов в декартовом произведении
        let resultLength = 1;
        for (const array of arrays) {
            resultLength *= array.length;
        }
    
        // создаём массив такого размера и перебираем номер элемента
        const result = new Array(resultLength);
        for (let i = 0; i < resultLength; ++i) {
            // один элемент декартова произведения
            const tuple = new Array(arrays.length);
            let tupleIndex = i;
            // цикл в обратном порядке нужен чтобы элементы начинали изменяться с конца
            for (let j = arrays.length - 1; j >= 0; --j) {
                const array = arrays[j];
                // имеем биекцию между индексами элементов декартова произведения (числа от 0 до resultLength-1)
                // и кортежами длины arrays.length, в которых каждый элемент — индекс в соответствующем массиве
                tuple[j] = array[tupleIndex % array.length];
                // целочисленное деление
                tupleIndex = Math.floor(tupleIndex / array.length);
            }
            result[i] = tuple;
        }
        return result;
    }
    

    递归方式

    在递归的帮助下,将创建笛卡尔积的元素:从一个空数组开始,在每个递归步骤中,将当前数组的所有元素都排序出来,笛卡尔积元素的类型化前缀的副本是创建后,将一个数组元素添加到副本中,并对生成的新前缀进行递归调用。

    function cartesian3(...arrays) {
        const result = [];
        // функция, которая будет рекурсивно вызываться
        // глубина рекурсии равна arrays.length
        // в процессе рекурсии функция будет создавать часть элемента декартова произведения
        // в конце рекусрии функция добавит созданный элемент в массив result
        const recursion = (tuplePart) => {
            if (tuplePart.length === arrays.length) {
                result.push(tuplePart);
            } else {
                const array = arrays[tuplePart.length];
                for (const element of array) {
                    // создаём копию tuplePart и добавляем в неё очередной элемент
                    const tuplePartWithNewElement = tuplePart.concat([element]);
                    recursion(tuplePartWithNewElement);
                }
            }
        };
        recursion([]);
        return result;
    }
    

    Snippet,它检查所有建议方法的工作的正确性

    // cartesian 1
    const cartesian1 = (...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [...d, e])), [[]]);
    
    // cartesian 1 (с комментариями)
    const cartesian1b = (...arrays) =>
        // итеративно получаем декартово произведение
        // нескольких первых массивов из arrays,
        // начиная с нуля массивов и пустого декартова произведения --- [[]]
        arrays.reduce((cartesianPart, array) =>
            // cartesianPart --- декартово произведение нескольких первых массивов из arrays
            cartesianPart.flatMap(cartesianPartTuple =>
                // array --- новый массив из arrays для добавления в декартово произведение
                array.map(arrayElement =>
                    // cartesianPartTuple --- массив-префикс одного из элементов декартова произведения
                    // arrayElement --- элемент одного из массива из arrays
                    [...cartesianPartTuple, arrayElement]
                )
            ),
            [[]]
        );
    
    // cartesian 2
    function cartesian2(...arrays) {
        // находим число элементов в декартовом произведении
        let resultLength = 1;
        for (const array of arrays) {
            resultLength *= array.length;
        }
    
        // создаём массив такого размера и перебираем номер элемента
        const result = new Array(resultLength);
        for (let i = 0; i < resultLength; ++i) {
            // один элемент декартова произведения
            const tuple = new Array(arrays.length);
            let tupleIndex = i;
            // цикл в обратном порядке нужен чтобы элементы начинали изменяться с конца
            for (let j = arrays.length - 1; j >= 0; --j) {
                const array = arrays[j];
                // имеем биекцию между индексами элементов декартова произведения (числа от 0 до resultLength-1)
                // и кортежами длины arrays.length, в которых каждый элемент — индекс в соответствующем массиве
                tuple[j] = array[tupleIndex % array.length];
                // целочисленное деление
                tupleIndex = Math.floor(tupleIndex / array.length);
            }
            result[i] = tuple;
        }
        return result;
    }
    
    // cartesian 3
    function cartesian3(...arrays) {
        const result = [];
        // функция, которая будет рекурсивно вызываться
        // глубина рекурсии равна arrays.length
        // в процессе рекурсии функция будет создавать часть элемента декартова произведения
        // в конце рекусрии функция добавит созданный элемент в массив result
        const recursion = (tuplePart) => {
            if (tuplePart.length === arrays.length) {
                result.push(tuplePart);
            } else {
                const array = arrays[tuplePart.length];
                for (const element of array) {
                    // создаём копию tuplePart и добавляем в неё очередной элемент
                    const tuplePartWithNewElement = tuplePart.concat([element]);
                    recursion(tuplePartWithNewElement);
                }
            }
        };
        recursion([]);
        return result;
    }
    
    // tests
    const cartesians = [
        cartesian1,
        cartesian1b,
        cartesian2,
        cartesian3
    ];
    
    const tests = [
        {
            name: 'product of zero arrays',
            input: [],
            output: [[]]
        },
        {
            name: 'product of single array',
            input: [
                [1, 2, 3]
            ],
            output: [
                [1],
                [2],
                [3]
            ]
        },
        {
            name: 'product of two arrays',
            input: [
                [1, 2, 3],
                [10, 20]
            ],
            output: [
                [1, 10],
                [1, 20],
                [2, 10],
                [2, 20],
                [3, 10],
                [3, 20]
            ]
        },
        {
            name: 'product of three arrays',
            input: [
                [1, 2],
                [10, 20],
                [100, 200, 300]
            ],
            output: [
                [1, 10, 100],
                [1, 10, 200],
                [1, 10, 300],
                [1, 20, 100],
                [1, 20, 200],
                [1, 20, 300],
                [2, 10, 100],
                [2, 10, 200],
                [2, 10, 300],
                [2, 20, 100],
                [2, 20, 200],
                [2, 20, 300]
            ]
        },
        {
            name: 'nested arrays',
            input: [
                [[1], [2], [3]],
                [[10], [20]]
            ],
            output: [
                [[1], [10]],
                [[1], [20]],
                [[2], [10]],
                [[2], [20]],
                [[3], [10]],
                [[3], [20]]
            ]
        }
    ];
    
    console.log('если нет сообщений об ошибке, то все функции работают правильно');
    cartesians.forEach((cartesian, index) => {
        for (const test of tests) {
            const output = cartesian(...test.input);
            const ok = JSON.stringify(output) === JSON.stringify(test.output);
            if (!ok) {
                console.log(`FAIL: cartesian function #${index + 1} for test ${test.name}`);
                console.log(`       expected: ${JSON.stringify(test.output)}`);
                console.log(`       received: ${JSON.stringify(output)}`);
            }
        }
    });

    准备好的库实现

    一些库具有笛卡尔积函数:

    • js 组合学→cartesianProduct
    • 笛卡尔积 - npm , github
    • d3-array → cross(仅适用于两个数组)
    • 6
  2. 4 редакцииuser456093
    2022-08-10T10:58:00Z2022-08-10T10:58:00Z

    多个数组的直接或笛卡尔积

    window.onload = function() {
      // несколько массивов
      let list1 = [1, 2, 3, 4, 5];
      let list2 = [10, 20];
      let list3 = [100, 200, 300];
    
      // декартово произведение трёх массивов
      let cp = cartesianProduct(list1, list2, list3);
    
      // вывод по столбцам
      let rows = 6;
      for (let i = 0; i < rows; i++) {
        let str = "";
        for (let j = 0; j < cp.length; j++)
          str += j % rows == i ? JSON.stringify(cp[j]) + " " : "";
        console.log(str);
      }
    };
    
    // декартово произведение нескольких массивов
    function cartesianProduct(...lists) {
      // входящие данные не равны null
      if (lists == null) return null;
      // промежуточный результат
      let cp = [[]];
      // обходим входящие массивы
      for (let i = 0; i < lists.length; i++) {
        // текущий массив
        let list = lists[i]
        // ненулевой и непустой массив
        if (list == null || list.length == 0) continue;
        // следующий промежуточный результат
        let next = [];
        // строки текущего промежуточного результата
        for (let j = 0; j < cp.length; j++) {
          // текущая строка
          let row = cp[j];
          // элементы текущего массива
          for (let k = 0; k < list.length; k++) {
            // текущий элемент
            let el = list[k];
            // новая строка для следующего
            // промежуточного результата
            let nRow = row.slice();
            nRow[row.length] = el;
            next[next.length] = nRow;
          }
        }
        // передать на следующую итерацию
        cp = next;
      }
      // итоговый результат
      return cp;
    }

    • 0

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    是否可以在 C++ 中继承类 <---> 结构?

    • 2 个回答
  • Marko Smith

    这种神经网络架构适合文本分类吗?

    • 1 个回答
  • Marko Smith

    为什么分配的工作方式不同?

    • 3 个回答
  • Marko Smith

    控制台中的光标坐标

    • 1 个回答
  • Marko Smith

    如何在 C++ 中删除类的实例?

    • 4 个回答
  • Marko Smith

    点是否属于线段的问题

    • 2 个回答
  • Marko Smith

    json结构错误

    • 1 个回答
  • Marko Smith

    ServiceWorker 中的“获取”事件

    • 1 个回答
  • Marko Smith

    c ++控制台应用程序exe文件[重复]

    • 1 个回答
  • Marko Smith

    按多列从sql表中选择

    • 1 个回答
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Suvitruf - Andrei Apanasik 什么是空? 2020-08-21 01:48:09 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5