【算法学习系列】07 - 无序数组中的局部最小值问题

文章目录

  • 说明
  • 约束条件
  • 简单说下思路
  • 解决方案
    • 随机无序数组样本生成器
    • 算法实现
    • 验证代码
    • 进行大样本随机测试验证算法正确性

说明


在算法中,局部最小值是指一个函数在一个局部范围内的最小值。

具体而言,如果一个函数在一个小区间内的取值都比该区间内的其他取值要小,那么该点就被称为局部最小值。

在算法中,寻找函数的局部最小值通常是一个重要的优化问题。

约束条件


  • 无序数组,相邻位置数组元素的的值大小不相等
  • 找到数组中某个局部最小值的下标

简单说下思路


  • 数组长度为 1 :局部最小值就是它自己。

  • 数组长度为 2 :较小的值是局部最小值。

  • 数组长度为 3 及以上

假设数组长度为 N(N >= 3)

C1:那么首先对比第 0 个位置和第 1 个位置的值大小;如果第 0 个位置的值更小,那第 0 个位置就是数组中的一个局部最小值位置。

C2:如果情况 C1 没找到最小值,那就继续寻找局部最小值,这时候可以对比第 N-1 个位置和第 N-2 个位置的值大小。如果第 N-1 个位置的值更小,那第 N-1 个位置就是数组中的一个局部最小值位置。

C3:如果情况 C1 和 C2 都没找到最小值,这时候说明第 0 个位置到第 1 个位置的值是下降趋势,第 N-2 个位置到第 N-1 个位置的值是上升趋势。如下图示:

在这里插入图片描述
由上图例子可知,在数组中,第 0 个位置和 第 5 个位置之间必存在一个局部最小值。

这时候我们对数组存在局部最小值的区间(0, N-1)进行二分,找到中间位置((0 + (N-1) )/ 2),然后对比中间位置跟相邻位置的值的大小:

1、如果此时中间位置的值最小,说明中间位置就是数组中的一个局部最小值位置;
2、如果中间位置的值不是最小值,那就可以知道中间位置跟相邻位置的值大小是上升还是下降趋势,然后进一步缩小存在局部最小值的区间范围。
3、再根据缩小后的区间继续进行二分判断,直至找到局部最小值,并且是肯定能找到的。

解决方案


有了上面的思路,我们下面开始来实现,并用对数器进行验证。

随机无序数组样本生成器

数组中相邻位置值的大小不相等。工具类如下:

package com.example.myapplication.util;

public class ArrayUtil {

    private volatile static ArrayUtil arrayUtil;

    private ArrayUtil() {
    }

    public static ArrayUtil instance(){
        if (arrayUtil == null){
            synchronized (ArrayUtil.class){
                if (arrayUtil == null){
                    arrayUtil = new ArrayUtil();
                }
            }
        }
        return arrayUtil;
    }

    // 生成无序随机数组,长度和元素值都是随机,且相邻位置的值不相等
    public static int[] getRandomArrayPosNotEqual(int maxLen, int maxValue){
        int randomLen = (int)(Math.random() * maxLen);
        int[] randomArr = new int[randomLen];
        if (randomLen > 0){
            randomArr[0] = (int)(Math.random() * maxValue);
            for (int i = 1; i < randomLen;i++){
                do {
                    randomArr[i] = (int)(Math.random() * maxValue);
                }while (randomArr[i] == randomArr[i - 1]);
            }
        }
        return randomArr;
    }

    // 生成随机数组,长度和元素值都是随机(相邻位置值可能相等)
    public static int[] getRandomArray(int maxLen, int maxValue){
        int randomLen = (int)(Math.random() * maxLen);
        int[] randomArr = new int[randomLen];
        if (randomLen > 0){
            for (int i = 0; i < randomLen;i++){
                randomArr[i] = (int)(Math.random() * maxValue);
            }
        }
        return randomArr;
    }

}

算法实现

算法问题解决方案,代码实现如下:

	// 实现局部最小值算法 找到数组中某个局部最小值的下标并返回
    // 数组无序,且相邻位置的元素值不相等
    private int getLocalMinInArray(int[] arr){
        if (arr == null || arr.length == 0){
            return -1;
        }
        int len = arr.length;
        if (len == 1){
            return 0;
        }
        if (arr[0] < arr[1]){
            return 0;
        }
        if (arr[len - 1] < arr[len - 2]){
            return (len - 1);
        }

        // 示例: [1262, 134, 701, 749, 465, 1333]
        int min = -1;
        int L = 0, R = len - 1;
        // 保证比较的数组中至少存在3个数组元素,防止边界条件异常:例如数组下标越界的异常
        while (L < (R - 1)){
            int mid = (L + R) / 2;
            if (arr[mid] < arr[mid - 1] && arr[mid] < arr[mid + 1]){
                return mid;
            }

            if (arr[mid] > arr[mid - 1]){
                R = mid - 1;
                continue;
            }

            if (arr[mid] > arr[mid + 1]){
                L = mid + 1;
                continue;
            }
        }

        // 比较数组中还剩两个值的情况 哪个小就返回哪个对应的下标
        min = L;
        if (arr[L] > arr[R]){
            min = R;
        }

        return min;
    }

代码实现跟上面说的思路是一样的。

  • 如果数组下标不存在,则返回 -1。

验证代码

获取到了数组中的局部最小值下标后,我们再来实现一个验证局部最小值是否正确的代码。如下:

	// 验证数组的局部最小值下标是否正确
    private boolean checkLocalMinIndex(int[] arr, int min){
        if (arr.length == 0){
            return (min == -1);
        }
        if (arr.length == 1){
            return (min == 0);
        }
        int minLeft = min - 1;
        int minRight = min + 1;
        if (minLeft == -1 && arr[min] < arr[minRight]){
            // 第一个是局部最小值
            return true;
        }
        if (minRight == (arr.length) && arr[min] < arr[minLeft]){
            // 最后一个是局部最小值
            return true;
        }
        // 中间某个位置是局部最小值
        if (arr[min] < arr[minLeft] && arr[min] < arr[minRight]){
            return true;
        }
        return false;
    }

进行大样本随机测试验证算法正确性

	private void testLocalMinInArray(){
        System.out.println(":> 测试开始");
        int maxLen = 10;
        int maxValue = 2000;
        int testCount = 100000;
        for (int i = 0;i < testCount;i++){
            int[] randomArr = ArrayUtil.instance().getRandomArrayPosNotEqual(maxLen, maxValue);
            int min = getLocalMinInArray(randomArr);
            if (!checkLocalMinIndex(randomArr, min)){
                System.out.println(":> randomArr = " + Arrays.toString(randomArr));
                System.out.println(":> 获取数组局部最小值下标min不正确,min = " + min);
                break;
            }

            if (i == testCount - 1){
                System.out.println(":> 最后一个数组:" + Arrays.toString(randomArr));
                System.out.println(":> 最后一个数组,局部最小值下标:" + min);
            }
        }
        System.out.println(":> 测试结束");
    }
  • 多次测试均通过,如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

故求取无序数组局部最小值下标算法正确。


“Peace Love Respect”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/26044.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++程序员的待遇怎么样?我来谈谈学好C++的五个关键点

有个学弟跟我谈到这样一个问题&#xff1a;现在我看到网上很多人都在讲&#xff0c;说这个做C程序员&#xff0c;尤其是本科毕业计算机专业&#xff0c;然后步入社会之后就能拿到月入过万。但是为什么自己找的这个工作啊&#xff0c;普遍在月薪六七千块钱左右&#xff0c;也就是…

利用OpenCV处理图像

OpenCV是非常流行的图像处理库&#xff0c;下面介绍一下其对图像的基本操作。 1. 安装与环境 安装还有点儿复杂的&#xff0c;但百度几篇博客基本能解决&#xff0c;这里就不多说了。 安装好后&#xff0c;要在工程中使用OpenCV的头文件和库&#xff0c;需要在CMakeLists.tx…

码住!IC设计常用工具合集!

芯片设计过程中&#xff0c;选择和使用适合的工具是非常重要的。芯片设计工具通常分为三类&#xff1a;EDA工具、模拟仿真工具和布局工具。 一、EDA工具 EDA工具是芯片设计的核心&#xff0c;它包括原理图绘制、逻辑综合、门级仿真工具和物理版图编辑等&#xff0c;可以帮助设计…

基于springboot+Redis的前后端分离项目(一)-【黑马点评】

&#x1f381;&#x1f381;资源文件分享 链接&#xff1a;https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA?pwdeh11 提取码&#xff1a;eh11 基于session和redis实现登录 &#xff08;一&#xff09;前言&#xff08;二&#xff09;导入资源&#xff08;三&#xff09;短信…

每日学术速递5.26

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Text2NeRF: Text-Driven 3D Scene Generation with Neural Radiance Fields 标题&#xff1a;Text2NeRF&#xff1a;具有神经辐射场的文本驱动 3D 场景生成 作者&#xff1a;Jingb…

Python程序设计基础:标识符、变量与赋值、输入输出

文章目录 一、标识符二、变量与赋值三、输入输出 一、标识符 Python对每个标识符的命名存在要求&#xff1a; 1、每个标识符必须以字母或下划线“_”开头&#xff0c;后跟字母、数字或下划线的任意序列。根据这个规则&#xff0c;以下都是Python中的合法名称&#xff1a;a&…

史上最全测试开发工具推荐(含自动化、性能、稳定性、抓包)

目录 一、UI自动化测试工具 1. uiautomator2 2. Appium 3. ATX-Test 4. Airtest 5. ATXServer2 6. STF 7. Appetizer 二、APP稳定性测试工具 8. UICrawler 9. Maxim 10. AppCrawler 三、APP性能测试工具 11. SoloPi 12. GT 四、抓包工具 13. AnyProxy 14. mi…

【滤波】设计卡尔曼滤波器

本文主要翻译自rlabbe/Kalman-and-Bayesian-Filters-in-Python的第8章节08-Designing-Kalman-Filters&#xff08;设计卡尔曼滤波器&#xff09;。 %matplotlib inline#format the book import book_format book_format.set_style()简介 在上一章节中&#xff0c;我们讨论了教…

【自然语言处理】【大模型】ChatGLM-6B模型结构代码解析(单机版)

ChatGLM-6B模型结构代码解析(单机版) ​ 本文介绍ChatGLM-6B的模型结构&#xff0c;代码来自https://huggingface.co/THUDM/chatglm-6b/blob/main/modeling_chatglm.py。 相关博客 【自然语言处理】【大模型】ChatGLM-6B模型结构代码解析(单机版) 【自然语言处理】【大模型】BL…

枚举_源码_分析

枚举源码分析 前言 这是所有Java语言枚举类型的公共基类。关于枚举的更多信息&#xff0c;包括编译器合成的隐式声明方法的描述&#xff0c;可以在Java的第8.9节中找到™ 语言规范。 请注意&#xff0c;当使用枚举类型作为集合的类型或映射中键的类型时&#xff0c;可以使用专…

斩获阿里offer,这份258页面试宝典也太顶了....

测试三年有余&#xff0c;很多新学到的技术不能再项目中得到实践&#xff0c;同时薪资的涨幅很低&#xff0c;于是萌生了跳槽大厂的想法 但大厂不是那么容易进的&#xff0c;前面惨败字节&#xff0c;为此我辛苦准备了两个月&#xff0c;又从小公司开始面试了半个月有余&#…

最系统的网络安全自学笔记+学习路线(超详细)

01 什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

虚拟机类加载机制

目录 1、概述 2、类加载的过程 1、过程总览 2、加载 3、链接-验证 4、链接-准备 5、链接-解析 6、初始化 7、总结 3、类加载的时机 4、类加载器 1、概述 2、类与类加载器 3、三层类加载器 4、双亲委派模型 5、其他加载策略 1、概述 一个Java类会被编译成一个Cl…

游戏封包加密方案解析

当下游戏市场已全面回暖&#xff0c;暑期档临近更将迎来大量新游上线&#xff0c;如此关键节点&#xff0c;游戏厂商应当更加注重游戏安全。 FairGuard发现近期游戏黑灰产攻击角度愈发刁钻&#xff0c;除了常见的内存修改外挂、注入挂&#xff0c;针对游戏封包破解的「脱机挂」…

chatgpt赋能python:Python图片处理教程

Python 图片处理教程 Python 是一种功能强大的编程语言&#xff0c;广泛应用于大量不同的行业和领域。其中之一是图像处理和分析。Python 提供了一个庞大的图像库&#xff0c;其拥有大量的工具和函数。Python 图像库具有高度的可扩展性&#xff0c;可以很容易地将其与其他库集…

Async 使用详解

Spring Boot异步调用Async 在实际开发中&#xff0c;有时候为了及时处理请求和进行响应&#xff0c;我们可能会多任务同时执行&#xff0c;或者先处理主任务&#xff0c;也就是异步调用&#xff0c;异步调用的实现有很多&#xff0c;例如多线程、定时任务、消息队列等&#xf…

【大数据分析】Hbase的基本原理

目录 Hbase 架构ClientZooKeeperMasterRegionServerHRegionStoreMemStoreStoreFileHFileHLog Hbase数据模型关于数据模型的其他概念Name SpaceTableRowColumnTime StampCell Hbase 架构 Client &#xff08;1&#xff09;.META.表&#xff0c;记录了用户所有表拆分出来的 Regi…

Redis数据类型之(哈希Hash和集合Set)

Redis数据类型之&#xff08;哈希Hash和集合Set&#xff09; 一定注意看红色注意项。 哈希&#xff08;Hash&#xff09;: Redis hash 是一个 string 类型的 field&#xff08;字段&#xff09; 和 value&#xff08;值&#xff09; 的映射表&#xff0c;hash 特别适合用于存…

龙芯2K1000实战开发-USB/PCIe/HDMI外设开发

文章目录 概要整体架构流程技术名词解释技术细节小结概要 提示:这里可以添加技术概要 本文主要针对2k1000的PCIE和USB外设的国产化设计 整体架构流程 提示:这里可以添加技术整体架构 使用2k1000自带的以太网pcie控制器,USB控制器。 考虑到龙芯没有HDMI接口,选用龙讯半…

springboot启动过程原理分析

前言 现在绝大多数java项目都上了Springboot框架, 因此深入理解Springboot框架的运行原理,能帮助我们更好的在Springboot框架下进行业务开发,同时能学习框架中优秀的设计思想, 本文主要是通过对Springboot源码的分析, 来理解整个springboot项目的启动流程. 因为Springboot不同…