千家信息网

怎么使用栈的记忆化搜索来加速子集和算法

发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,本篇内容主要讲解"怎么使用栈的记忆化搜索来加速子集和算法",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"怎么使用栈的记忆化搜索来加速子集和算法"吧!所谓子集
千家信息网最后更新 2025年02月01日怎么使用栈的记忆化搜索来加速子集和算法

本篇内容主要讲解"怎么使用栈的记忆化搜索来加速子集和算法",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"怎么使用栈的记忆化搜索来加速子集和算法"吧!

所谓子集和就是在一个数组中找出它的子集,使得该子集的和等于某个固定值。

一般我们都是使用递归加回溯的方式来处理的,代码如下(此处我们只找出一组满足的条件即可)

public class SubSet {private List list = new ArrayList<>();   //用于存放求取子集中的元素    @Getter    private List res = new ArrayList<>();    //求取数组列表中元素和    public int getSum(List list) {int sum = 0;        for(int i = 0;i < list.size();i++)            sum += list.get(i);        return sum;    }public void getSubSet(int[] A, int m, int step) {if (res.size() > 0) {return;        }while(step < A.length) {list.add(A[step]);            if (getSum(list) == m) {if (getSum(res) == 0) {res.addAll(list);                }            }            step++;            getSubSet(A, m, step);            list.remove(list.size() - 1);   //回溯执行语句,删除列表最后一个元素        }    }public static void main(String[] args) {        SubSet test = new SubSet();        int[] A = new int[6];        for(int i = 0;i < 6;i++) {            A[i] = i + 1;        }        test.getSubSet(A, 8, 0);        System.out.println(test.getRes());    }}

运行结果

[1, 2, 5]

但是这个算法的时间复杂度非常高,是NP级别的。如果数据量比较大的时候,将很难完成运算。

现在我们用栈和哈希缓存来加速这个算法。主要是缓存计算结果,不用每次都去getSum中把list的和算一遍。其思想主要是记忆化搜索,可以参考本人这篇博客动态规划、回溯、贪心,分治

public class SubSet {private List list = new ArrayList<>();   //用于存放求取子集中的元素    @Getter    private List res = new ArrayList<>();    private Deque deque = new ArrayDeque<>();    private Map map = new HashMap<>();    //求取数组列表中元素和    public int getSum(List list) {int sum = 0;        for(int i = 0;i < list.size();i++)            sum += list.get(i);        return sum;    }public void getSubSet(int[] A, int m, int step) {if (res.size() > 0) {return;        }while(step < A.length) {list.add(A[step]);            if (!map.containsKey(deque.toString())) {int sum = getSum(list);                deque.push(A[step]);                map.put(deque.toString(),sum);                if (sum == m) {if (getSum(res) == 0) {res.addAll(list);                    }                }            }else {int sum = map.get(deque.toString()) + A[step];                deque.push(A[step]);                map.put(deque.toString(),sum);                if (sum == m) {if (getSum(res) == 0) {res.addAll(list);                    }                }            }            step++;            getSubSet(A, m, step);            list.remove(list.size() - 1);   //回溯执行语句,删除列表最后一个元素            deque.pop();        }    }public static void main(String[] args) {        SubSet test = new SubSet();        int[] A = new int[6];        for(int i = 0;i < 6;i++) {            A[i] = i + 1;        }        test.getSubSet(A, 8, 0);        System.out.println(test.getRes());    }}

运算结果

[1, 2, 5]

但C#无法满足获取栈的值,只能获取栈的类型,如果我们用遍历的方式去获取栈的值又回到了以前NP级的时间复杂度,故直接使用数字来做哈希表的键。内容如下

using System;using System.Collections.Generic;using System.Collections;using System.Text.RegularExpressions;using System.Linq;using System.Text;using System.Threading.Tasks;namespace ConsoleApplication1{    class Program    {        private class Oranize        {            public List array = new List();            public List res = new List();            public Stack stack = new Stack();            public Hashtable table = new Hashtable();            public decimal index = 0;            public decimal getSum(List list)            {                decimal sum = 0;                for (int i = 0; i < list.Count; i++)                {                    sum += list[i];                }                return sum;            }            public String stackValue(Stack stack)            {                StringBuilder sb = new StringBuilder();                foreach (decimal s in stack)                {                    sb.Append(s.ToString());                }                return sb.ToString();            }            public void org(decimal[] arr,decimal all, int step)            {                if (res.Count > 0)                {                    return;                }                while (step < arr.Length)                {                    array.Add(arr[step]);                                        if (!table.ContainsKey(index.ToString()))                    {                        decimal sum = getSum(array);                        stack.Push(index);                        table.Add(stack.Peek().ToString(), sum);                        if (sum == all)                        {                            if (getSum(res) == 0)                            {                                foreach (decimal a in array)                                {                                    res.Add(a);                                }                            }                        }                    }                    else                    {                        decimal sum = 0;                        if (stack.Count > 0)                        {                            sum = Convert.ToDecimal(table[stack.Peek().ToString()]) + arr[step];                        }                        else                        {                            sum = Convert.ToDecimal(table["0"]) + arr[step];                        }                        index++;                        stack.Push(index);                        if (table.ContainsKey(stack.Peek().ToString()))                        {                            table.Remove(stack.Peek().ToString());                        }                        table.Add(stack.Peek().ToString(), sum);                        if (sum == all)                        {                            if (getSum(res) == 0)                            {                                foreach (decimal a in array)                                {                                    res.Add(a);                                }                            }                        }                    }                    step++;                    org(arr, all, step);                    array.RemoveAt(array.Count - 1);                    stack.Pop();                }            }        }        static void Main(string[] args)        {            decimal[] A = new decimal[6];            for (int i = 0; i < 6; i++)            {                A[i] = i + 1;            }            Oranize oranize = new Oranize();            oranize.org(A, 8, 0);            foreach (decimal r in oranize.res)            {                Console.Write(r + ",");            }            Console.ReadLine();        }    }}

这里我们可以看到如果使用stackValue来获取栈的各个值的字符串是不可取的,同样会非常慢。

由于C#本身的Hashtable在数据量大的情况下存在溢出风险,所以我们要重写哈希表。重写的哈希表的每个节点由红黑树组成,由于我们并不需要删除哈希表内的元素,所以就不写红黑树和哈希表的删除方法。

        private class RedBlackTreeMap        {            private static bool RED = true;            private static bool BLACK = false;            private class Node            {                public String key;                public decimal value;                public Node left;                public Node right;                public bool color;                public Node(String key,decimal value,Node left,Node right,bool color)                {                    this.key = key;                    this.value = value;                    this.left = left;                    this.right = right;                    this.color = color;                }                public Node(String key): this(key, 0, null, null, RED)                { }                public Node(String key,decimal value): this(key, value, null, null, RED)                { }                                                }            private Node root;            private int size;            public ISet keySet = new HashSet();            public RedBlackTreeMap()            {                root = null;                size = 0;            }            private bool isRed(Node node)            {                if (node == null)                {                    return BLACK;                }                return node.color;            }            private Node leftRotate(Node node)            {                Node ret = node.right;                Node retLeft = ret.left;                node.right = retLeft;                ret.left = node;                ret.color = node.color;                node.color = RED;                return ret;            }            private Node rightRotate(Node node)            {                Node ret = node.left;                Node retRight = ret.right;                node.left = retRight;                ret.right = node;                ret.color = node.color;                node.color = RED;                return ret;            }            private void flipColors(Node node)            {                node.color = RED;                node.left.color = BLACK;                node.right.color = BLACK;            }            public void add(String key,decimal value)            {                root = add(root, key, value);                keySet.Add(key);            }            private Node add(Node node,String key,decimal value)            {                if (node == null)                {                    size++;                    return new Node(key, value);                }                if (key.CompareTo(node.key) < 0)                {                    node.left = add(node.left, key, value);                }else if (key.CompareTo(node.key) > 0)                {                    node.right = add(node.right, key, value);                }else                {                    node.value = value;                }                if (isRed(node.right) && !isRed(node.left))                {                    node = leftRotate(node);                }                if (isRed(node.left) && isRed(node.left.left))                {                    node = rightRotate(node);                }                if (isRed(node.left) && isRed(node.right))                {                    flipColors(node);                }                return node;            }            public bool contains(String key)            {                return getNode(root, key) != null;            }            public decimal get(String key)            {                Node node = getNode(root, key);                return node == null ? 0 : node.value;            }            public void set(String key,decimal value)            {                Node node = getNode(root, key);                if (node == null)                {                    throw new ArgumentException(key + "不存在");                }                node.value = value;            }            public int getSize()            {                return size;            }            public bool isEmpty()            {                return size == 0;            }            private Node getNode(Node node,String key)            {                if (node == null)                {                    return null;                }                if (key.CompareTo(node.key) == 0)                {                    return node;                }else if (key.CompareTo(node.key) < 0)                {                    return getNode(node.left, key);                }else                {                    return getNode(node.right, key);                }            }        }        private class HashFind        {            private int[] capacity = {53,97,193,389,769,1543,3079,6151,12289,24593,            49157,98317,196613,393241,786433,1572869,3145739,            6291469,12582917,25165843,50331653,100663319,            201326611,402653189,805306457,1610612741};            //容忍度上界            private static int upperTol = 10;            //容忍度下届            private static int lowerTol = 2;            private int capacityIndex = 0;            private RedBlackTreeMap[] tables;            private int M;            private int size;            public HashFind()            {                this.M = capacity[capacityIndex];                this.size = 0;                tables = new RedBlackTreeMap[M];                for (int i = 0; i < M; i++)                {                    tables[i] = new RedBlackTreeMap();                }            }            private int hash(String key)            {                return (key.GetHashCode() & 0x7fffffff) % M;            }            public void add(String key,decimal value)            {                RedBlackTreeMap map = tables[hash(key)];                if (map.contains(key))                {                    map.add(key, value);                }else                {                    map.add(key, value);                    size++;                    if (size >= upperTol * M && capacityIndex + 1 < capacity.Length)                    {                        capacityIndex++;                        resize(capacity[capacityIndex]);                    }                }            }            public bool contains(String key)            {                int index = hash(key);                return tables[index].contains(key);            }            public decimal get(String key)            {                int index = hash(key);                return tables[index].get(key);            }            public void set(String key,decimal value)            {                int index = hash(key);                RedBlackTreeMap map = tables[index];                if(!map.contains(key))                {                    throw new ArgumentException(key + "不存在");                }                map.add(key, value);            }            public int getSize()            {                return size;            }            public bool isEmpty()            {                return size == 0;            }            private void resize(int newM)            {                RedBlackTreeMap[] newTables = new RedBlackTreeMap[newM];                for (int i = 0; i < newM; i++)                {                    newTables[i] = new RedBlackTreeMap();                }                int oldM = this.M;                this.M = newM;                for (int i = 0; i < oldM; i++)                {                    RedBlackTreeMap map = tables[i];                    foreach (String key in map.keySet)                    {                        int index = hash(key);                        newTables[index].add(key, map.get(key));                    }                }                this.tables = newTables;            }        }        private class Oranize        {            public List array = new List();            public List res = new List();            public Stack stack = new Stack();            public HashFind table = new HashFind();            public decimal index = 0;            public decimal getSum(List list)            {                decimal sum = 0;                for (int i = 0; i < list.Count; i++)                {                    sum += list[i];                }                return sum;            }            //public String stackValue(Stack stack)            //{            //    StringBuilder sb = new StringBuilder();            //    foreach (decimal s in stack)            //    {            //        sb.Append(s.ToString());            //    }            //    return sb.ToString();            //}            public void org(decimal[] arr, decimal all, int step)            {                if (res.Count > 0)                {                    return;                }                while (step < arr.Length)                {                    array.Add(arr[step]);                    if (!table.contains(index.ToString()))                    {                        decimal sum = getSum(array);                        stack.Push(index);                        table.add(stack.Peek().ToString(), sum);                        if (sum == all)                        {                            if (getSum(res) == 0)                            {                                foreach (decimal a in array)                                {                                    res.Add(a);                                }                            }                        }                    }                    else                    {                        decimal sum = 0;                        if (stack.Count > 0)                        {                            sum = Convert.ToDecimal(table.get(stack.Peek().ToString())) + arr[step];                        }                        else                        {                            sum = Convert.ToDecimal(table.get("0")) + arr[step];                        }                        index++;                        stack.Push(index);                        if (!table.contains(stack.Peek().ToString()))                        {                            table.add(stack.Peek().ToString(), sum);                        }                        if (sum == all)                        {                            if (getSum(res) == 0)                            {                                foreach (decimal a in array)                                {                                    res.Add(a);                                }                            }                        }                    }                    step++;                    org(arr, all, step);                    array.RemoveAt(array.Count - 1);                    stack.Pop();                }            }        }

虽然该算法进行了加速,但是能否算出,依然在于数组元素的个数所组成的和的组合数,比如有1、2、3、4四个数,则这四个数的和的组合数为1、2、3、4、1+2、1+2+3、1+2+4、1+2+3+4、1+3、1+3+4、1+4、2+3、2+3+4、2+4、3+4总共15个。

我们可以用计算组合数算法来进行验证,该算法也是使用递归加记忆化搜索的方式

public class Combine {private static Map map= new HashMap<>();    /**     * 计算从m个元素中拿出n个元素的组合数     * @param m     * @param n     * @return     */    private static long comb(int m,int n){        String key= m+","+n;        if(n == 0)return 1;        if (n == 1)return m;        if(n > m / 2)return comb(m,m-n);        if(n > 1){if(!map.containsKey(key))map.put(key, comb(m-1,n-1)+comb(m-1,n));            return map.get(key);        }return -1;    }public static void main(String[] args) {long total = 0;        for (int i = 1 ; i <= 4; i++) {            total += comb(4,i);        }        System.out.println(total);    }}

运行结果

15

我们现在的主要目的是寻找可计算的节点,我们可以先给出一个比较大的数,比如一个数组中有40个元素

public static void main(String[] args) {long total = 0;    for (int i = 1 ; i <= 40; i++) {        total += comb(40,i);    }    System.out.println(total);}

运行结果

1099511627775

由结果可知,40个数的组合数达到了万亿级别,一般我们计算机的计算级数量在亿级别就差不多了,再多的话就比较难算的出来了。当然这里我的个人建议是数组元素数量在28个

public static void main(String[] args) {long total = 0;    for (int i = 1 ; i <= 28; i++) {        total += comb(28,i);    }    System.out.println(total);}

运行结果

268435455

这里是2.6亿,最后我们来看一下30的组合数

public static void main(String[] args) {long total = 0;    for (int i = 1 ; i <= 30; i++) {        total += comb(30,i);    }    System.out.println(total);}

运行结果

1073741823

运行结果为10亿,所以我们可以看出从28到30,增长的组合数绝对不是一点点。这是一个几何级数的增长。

到此,相信大家对"怎么使用栈的记忆化搜索来加速子集和算法"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

元素 结果 子集 算法 组合 数组 哈希 运行 搜索 记忆 个数 内容 方式 级别 复杂 复杂度 容忍度 数据 数量 方法 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 部队外包软件开发 数据库所有表的大小 数据库的性能优化有哪些方 中学网络安全应急机制制度 数据库能存储结构化的数据 软件开发前后端打通是什么意思 闻政绩效管理系统软件开发部 邮箱服务器测试 大学数据库技术与应用视频 中兴数据库访问中间件含义 中标麒麟服务器高级操作系统 视频服务器和流媒体服务器 数据库中外部码能为空吗 如何做软件开发人员 软件开发桌面指纹仪哪家便宜 命运2官网服务器离线 个税系统软件开发成本 管理软件开发的质量 网络安全工会活动方案 国知局数据库 雨人网络安全股份 软件开发在会计上属于什么行业 鹤壁市科恩网络技术有限公司 网络安全技术与数据库实训报告 新兴网络技术对宪法的影响 408 数据库 浙江炒币量化交易软件开发报价 网络安全支队是做什么 如何查看应用服务器设置 大学网络技术证
0